From 74ad9f74f18b3f2df35b8cbc00ebf94b09bd0607 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 12 Oct 2026 14:26:10 -0400 Subject: [PATCH 1/5] Add Oklahoma state supplementation for 2026 --- changelog.d/ok-ssp-2026.added.md | 1 + .../household/household_state_benefits.yaml | 4 + .../hud/annual_income/sources/unearned.yaml | 1 + .../exclusions/child_support_share.yaml | 10 + .../ok/dhs/ssp/blind_earned_exclusion.yaml | 11 + .../states/ok/dhs/ssp/child_allocation.yaml | 10 + .../dhs/ssp/ineligible_spouse_standard.yaml | 11 + .../states/ok/dhs/ssp/maximum_payment.yaml | 10 + .../usda/snap/income/sources/unearned.yaml | 7 +- policyengine_us/programs.yaml | 12 + .../gov/states/ok/dhs/ssp/integration.yaml | 298 ++++++++++++++++++ .../gov/states/ok/dhs/ssp/ok_ssp.yaml | 170 ++++++++++ .../gov/states/ok/dhs/ssp/_ok_ssp_income.py | 66 ++++ .../variables/gov/states/ok/dhs/ssp/ok_ssp.py | 43 +++ .../ok/dhs/ssp/ok_ssp_countable_income.py | 14 + .../gov/states/ok/dhs/ssp/ok_ssp_eligible.py | 33 ++ .../ok/dhs/ssp/ok_ssp_has_essential_spouse.py | 9 + .../states/ok/dhs/ssp/ok_ssp_is_excluded.py | 10 + .../ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py | 10 + .../ok/dhs/ssp/ok_ssp_own_countable_income.py | 14 + .../dhs/ssp/ok_ssp_spouse_child_allocation.py | 21 ++ .../income/spm_unit/spm_unit_benefits.py | 1 + 22 files changed, 762 insertions(+), 4 deletions(-) create mode 100644 changelog.d/ok-ssp-2026.added.md create mode 100644 policyengine_us/parameters/gov/ssa/ssi/income/exclusions/child_support_share.yaml create mode 100644 policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml create mode 100644 policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml create mode 100644 policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml create mode 100644 policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_countable_income.py create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_eligible.py create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_has_essential_spouse.py create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_excluded.py create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_own_countable_income.py create mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py diff --git a/changelog.d/ok-ssp-2026.added.md b/changelog.d/ok-ssp-2026.added.md new file mode 100644 index 00000000000..c0067e2b9ae --- /dev/null +++ b/changelog.d/ok-ssp-2026.added.md @@ -0,0 +1 @@ +Add Oklahoma State Supplemental Payment for 2026. diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index d2cbd6ba2d4..7cfab501ec3 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -78,6 +78,7 @@ values: # DC benefits - dc_ossp # Kansas benefits + - ok_ssp - ks_sspp # Hawaii benefits - hi_oss @@ -176,6 +177,7 @@ values: # DC benefits - dc_ossp # Kansas benefits + - ok_ssp - ks_sspp # Hawaii benefits - hi_oss @@ -279,6 +281,7 @@ values: - dc_ossp - dc_child_care_subsidies # Kansas benefits + - ok_ssp - ks_sspp # Hawaii benefits - hi_oss @@ -382,6 +385,7 @@ values: - dc_ossp - dc_child_care_subsidies # Kansas benefits + - ok_ssp - ks_sspp # Hawaii benefits - hi_oss diff --git a/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml b/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml index d2b2bbd8675..f87ca59ee1f 100644 --- a/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml @@ -19,6 +19,7 @@ values: - strike_benefits - social_security - ssi + - ok_ssp - general_assistance - unemployment_compensation - child_support_received diff --git a/policyengine_us/parameters/gov/ssa/ssi/income/exclusions/child_support_share.yaml b/policyengine_us/parameters/gov/ssa/ssi/income/exclusions/child_support_share.yaml new file mode 100644 index 00000000000..a5f0fdb42a5 --- /dev/null +++ b/policyengine_us/parameters/gov/ssa/ssi/income/exclusions/child_support_share.yaml @@ -0,0 +1,10 @@ +description: The Social Security Administration excludes this share of child support received + by an eligible child from SSI unearned income. +values: + '2026-01-01': 0.3333333333333333 +metadata: + unit: /1 + label: SSI child support exclusion share + reference: + - title: 20 CFR 416.1124(c)(11) + href: https://www.law.cornell.edu/cfr/text/20/416.1124 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml new file mode 100644 index 00000000000..52242f0e28d --- /dev/null +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml @@ -0,0 +1,11 @@ +description: Oklahoma excludes this additional amount of monthly earned income of a blind individual before + the standard earned income exclusions. +values: + '2026-01-01': 500 +metadata: + unit: currency-USD + label: Oklahoma SSP blind earnings exclusion + reference: + - title: OAC 317:35-5-42(g)(5) + href: https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html + period: month diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml new file mode 100644 index 00000000000..b778cba996c --- /dev/null +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml @@ -0,0 +1,10 @@ +description: Oklahoma uses this allocation for an ineligible child for its State Supplemental Payment. +values: + '2026-01-01': 498 +metadata: + unit: currency-USD + label: Oklahoma SSP allocation for an ineligible child + reference: + - title: Appendix C-1 Schedule VIII A, C + href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 + period: month diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml new file mode 100644 index 00000000000..f329bed8360 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml @@ -0,0 +1,11 @@ +description: Oklahoma uses this need standard for an individual with an ineligible or essential spouse + for its State Supplemental Payment. +values: + '2026-01-01': 1532 +metadata: + unit: currency-USD + label: Oklahoma SSP need standard for an individual with an ineligible or essential spouse + reference: + - title: Appendix C-1 Schedule VIII A, C + href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 + period: month diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml new file mode 100644 index 00000000000..1bb3c60c1ae --- /dev/null +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml @@ -0,0 +1,10 @@ +description: Oklahoma uses this maximum payment per recipient for its State Supplemental Payment. +values: + '2026-01-01': 40 +metadata: + unit: currency-USD + label: Oklahoma SSP maximum payment per recipient + reference: + - title: Appendix C-1 Schedule VIII A, C + href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 + period: month diff --git a/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml index 6f94d3d09b2..de223268792 100644 --- a/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml @@ -36,6 +36,7 @@ values: - hi_oss - id_aabd - in_ssp + - ok_ssp - ks_sspp - ky_ssp - la_oss @@ -95,9 +96,7 @@ values: metadata: reference: - - title: 7 CFR 273.9(b)(2) - href: https://www.law.cornell.edu/cfr/text/7/273.9#b_2 - - title: 7 CFR 273.9(c)(1) - href: https://www.law.cornell.edu/cfr/text/7/273.9#c_1 + - title: 7 CFR 273.9(b)(2) and (c)(1), (5) + href: https://www.law.cornell.edu/cfr/text/7/273.9 - title: POMS SI 01401.001 General Information about State Supplementation href: https://secure.ssa.gov/poms.nsf/lnx/0501401001 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 05cb1d13747..32c857cc063 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1090,6 +1090,18 @@ programs: tracks. Unmodeled paths: grandfathered medically-needy-in-medical- institution recipients per WAC 388-478-0055(2)(b); DDA-determined / PVL track per (2)(f); and foster child in BRS per (2)(g). + - state: OK + status: partial + name: Oklahoma SSP + full_name: Oklahoma State Supplemental Payment + variable: ok_ssp + parameter_prefix: gov.states.ok.dhs.ssp + verified_years: '2026' + notes: Optional community SSP, including the distinct ineligible/essential spouse tests, + blind earnings exclusion and child allocations. Procedural exclusions and individual + TANF membership require inputs. Override countable income for specialized work expenses, + sponsor deeming or state-specific parental deeming; grandfathered institutional payments + are excluded. - id: social_security name: Social Security diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml new file mode 100644 index 00000000000..7b20c6b1715 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml @@ -0,0 +1,298 @@ +- name: Case 1, eligible couple maximum. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + second: + age: 65 + marital_units: + couple: + members: + - first + - second + families: + family: + members: + - first + - second + households: + hh: + members: + - first + - second + state_code: OK + output: + ok_ssp: + - 40 + - 40 +- name: Case 2, odd dollar couple grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + social_security_retirement: 18600 + second: + age: 65 + marital_units: + couple: + members: + - first + - second + families: + family: + members: + - first + - second + households: + hh: + members: + - first + - second + state_code: OK + output: + ok_ssp: + - 20 + - 21 +- name: Case 3, ineligible spouse income leaves partial grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + second: + age: 40 + social_security_retirement: 18480 + marital_units: + couple: + members: + - first + - second + families: + family: + members: + - first + - second + households: + hh: + members: + - first + - second + state_code: OK + output: + ok_ssp: + - 12 + - 0 +- name: Case 4, own income test is separate from combined test. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + social_security_retirement: 12720 + second: + age: 40 + marital_units: + couple: + members: + - first + - second + families: + family: + members: + - first + - second + households: + hh: + members: + - first + - second + state_code: OK + output: + ok_ssp: + - 0 + - 0 +- name: Case 5, essential spouse uses combined test. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + social_security_retirement: 12720 + ok_ssp_has_essential_spouse: true + second: + age: 40 + marital_units: + couple: + members: + - first + - second + families: + family: + members: + - first + - second + households: + hh: + members: + - first + - second + state_code: OK + output: + ok_ssp: + - 40 + - 0 +- name: Case 6, spouse on TANF income is excluded. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + second: + age: 40 + social_security_retirement: 60000 + ok_ssp_is_in_tanf_unit: true + marital_units: + couple: + members: + - first + - second + families: + family: + members: + - first + - second + households: + hh: + members: + - first + - second + state_code: OK + output: + ok_ssp: + - 40 + - 0 +- name: Case 7, spouse child allocation supports recipient. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + second: + age: 40 + social_security_retirement: 21600 + child: + age: 10 + marital_units: + couple: + members: + - first + - second + child: + members: + - child + families: + family: + members: + - first + - second + - child + households: + hh: + members: + - first + - second + - child + state_code: OK + output: + ok_ssp: + - 40 + - 0 + - 0 +- name: Case 8, child income reduces spouse allocation. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + second: + age: 40 + social_security_retirement: 21600 + child: + age: 10 + social_security_survivors: 4800 + marital_units: + couple: + members: + - first + - second + child: + members: + - child + families: + family: + members: + - first + - second + - child + households: + hh: + members: + - first + - second + - child + state_code: OK + output: + ok_ssp: + - 0 + - 0 + - 0 +- name: Case 9, resources of ineligible spouse count. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + second: + age: 40 + ssi_countable_resources: 3001 + marital_units: + couple: + members: + - first + - second + families: + family: + members: + - first + - second + households: + hh: + members: + - first + - second + state_code: OK + output: + ok_ssp: + - 0 + - 0 +- name: Case 10, annual supplement and SNAP income. + period: '2026' + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + output: + ok_ssp: 480 + snap_unearned_income: 12408 + hud_unearned_income: 12408 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml new file mode 100644 index 00000000000..f2c407b8aee --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml @@ -0,0 +1,170 @@ +- name: Case 1, full SSI gets maximum supplement. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + output: + ok_ssp: 40 +- name: Case 2, small cash income gets maximum supplement. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + social_security_retirement: 240 + output: + ok_ssp: 40 +- name: Case 3, state only payment above SSI limit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + social_security_retirement: 12240 + output: + ok_ssp: 34 +- name: Case 4, income at state need standard. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + social_security_retirement: 12648 + output: + ok_ssp: 0 +- name: Case 5, earnings without special blind exclusion. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + employment_income: 30000 + output: + ok_ssp: 0 +- name: Case 6, extra five hundred dollar exclusion for blind earnings. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + employment_income: 30000 + is_blind: true + output: + ok_ssp: 40 +- name: Case 7, income sources round before exclusions. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + social_security_retirement: 12245.88 + output: + ok_ssp: 34 +- name: Case 8, half dollar source rounds upward. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + social_security_retirement: 12246 + output: + ok_ssp: 33 +- name: Case 9, interest from resources is excluded. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + social_security_retirement: 12240 + interest_income: 1200 + output: + ok_ssp: 34 +- name: Case 10, countable resources at limit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + ssi_countable_resources: 2000 + output: + ok_ssp: 40 +- name: Case 11, excess countable resources. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + ssi_countable_resources: 2001 + output: + ok_ssp: 0 +- name: Case 12, blind child is eligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 10 + is_blind: true + output: + ok_ssp: 40 +- name: Case 13, nonresident. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: TX + age: 65 + output: + ok_ssp: 0 +- name: Case 14, not aged blind or disabled. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 40 + output: + ok_ssp: 0 +- name: Case 15, opt out. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + takes_up_ssi_if_eligible: false + output: + ok_ssp: 0 +- name: Case 16, TANF member cannot also get SSP. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + ok_ssp_is_in_tanf_unit: true + output: + ok_ssp: 0 +- name: Case 17, excluded facility or sanction. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + ok_ssp_is_excluded: true + output: + ok_ssp: 0 +- name: Case 18, institutional SSI is outside optional community program. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + ssi_federal_living_arrangement: MEDICAL_TREATMENT_FACILITY + output: + ok_ssp: 0 +- name: Case 19, VTR does not increase the forty dollar maximum. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + ssi_federal_living_arrangement: ANOTHER_PERSONS_HOUSEHOLD + output: + ok_ssp: 40 diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py new file mode 100644 index 00000000000..a715bf21c2c --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py @@ -0,0 +1,66 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.ssa.ssi.eligibility.income._apply_ssi_exclusions import ( + _apply_ssi_exclusions, +) + + +def ok_ssp_income(person, period, parameters, combined): + p = parameters(period).gov.states.ok.dhs.ssp + ssi = parameters(period).gov.ssa.ssi + # 317:35-5-42(i) rounds each monthly source before applying exclusions. + earned = sum( + np.floor(max_(person(source, period), 0) + 0.5) + for source in ssi.income.sources.earned + ) + unearned = sum( + np.floor(max_(person(source, period), 0) + 0.5) + for source in ssi.income.sources.unearned + if source not in ("interest_income", "dividend_income") + ) + child = person("is_child", period.this_year) + unearned = max_( + unearned + - child + * np.floor(max_(person("child_support_received", period), 0) + 0.5) + * ssi.income.exclusions.child_support_share, + 0, + ) + unearned = ( + unearned + + person("ssi_unearned_income_deemed_from_ineligible_parent", period) + + person("ssi_in_kind_support_and_maintenance", period) + ) + earned = max_( + earned + - person("ssi_blind_or_disabled_working_student_exclusion", period) + - person("is_blind", period.this_year) * p.blind_earned_exclusion, + 0, + ) + benefit = person("ssi", period) + # VTR shelter is counted separately, without the $20 income exclusion. + arrangement = person("ssi_federal_living_arrangement", period) + vtr = arrangement == arrangement.possible_values.ANOTHER_PERSONS_HOUSEHOLD + joint = person("ssi_couple_computation_applies", period) + shelter = ( + vtr + * where(joint, ssi.amount.couple / 2, ssi.amount.individual) + * ssi.amount.one_third_reduction_rate + ) + if combined: + eligible = person("ok_ssp_eligible", period) + allocation = ~eligible * person("ok_ssp_spouse_child_allocation", period) + earned = max_(earned - max_(allocation - unearned, 0), 0) + unearned = max_(unearned - allocation, 0) + included = ~person("ok_ssp_is_in_tanf_unit", period) + earned = person.marital_unit.sum(earned * included) + unearned = person.marital_unit.sum(unearned * included) + benefit = person.marital_unit.sum(benefit * included) + shelter = person.marital_unit.sum(shelter * included) + countable = ( + _apply_ssi_exclusions( + earned * MONTHS_IN_YEAR, unearned * MONTHS_IN_YEAR, parameters, period + ) + / MONTHS_IN_YEAR + ) + # SSI is needs-based income and does not consume the general exclusion. + return countable + benefit + shelter diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py new file mode 100644 index 00000000000..df32798ac6b --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py @@ -0,0 +1,43 @@ +from policyengine_us.model_api import * + + +class ok_ssp(Variable): + value_type = float + entity = Person + label = "Oklahoma State Supplemental Payment" + definition_period = MONTH + unit = USD + reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" + defined_for = "ok_ssp_eligible" + + def formula(person, period, parameters): + p = parameters(period).gov.states.ok.dhs.ssp + ssi = parameters(period).gov.ssa.ssi.amount + individual_standard = ssi.individual + p.maximum_payment + couple_standard = ssi.couple + 2 * p.maximum_payment + recipients = person.marital_unit.sum(person("ok_ssp_eligible", period)) + joint = recipients == 2 + has_spouse = person.marital_unit.nb_persons() == 2 + spouse_tanf = ( + person.marital_unit.sum(person("ok_ssp_is_in_tanf_unit", period)) > 0 + ) + with_spouse = has_spouse & ~spouse_tanf + own = person("ok_ssp_own_countable_income", period) + combined = person("ok_ssp_countable_income", period) + standard = where( + joint, + couple_standard, + where(with_spouse, p.ineligible_spouse_standard, individual_standard), + ) + income = where(with_spouse, combined, own) + gap = max_(standard - income, 0) + # Assign an indivisible dollar to the second member of the couple. + first_share = np.floor(gap / 2) + first = person.marital_unit.reference_entity.members_position == 0 + payment = where(joint, where(first, first_share, gap - first_share), gap) + own_test = ( + joint + | person("ok_ssp_has_essential_spouse", period) + | (own < individual_standard) + ) + return own_test * min_(payment, p.maximum_payment) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_countable_income.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_countable_income.py new file mode 100644 index 00000000000..6c7857ccb64 --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_countable_income.py @@ -0,0 +1,14 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.ok.dhs.ssp._ok_ssp_income import ok_ssp_income + + +class ok_ssp_countable_income(Variable): + value_type = float + entity = Person + label = "Oklahoma SSP countable income" + definition_period = MONTH + unit = USD + reference = "https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html" + + def formula(person, period, parameters): + return ok_ssp_income(person, period, parameters, True) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_eligible.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_eligible.py new file mode 100644 index 00000000000..e4710de54c6 --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_eligible.py @@ -0,0 +1,33 @@ +from policyengine_us.model_api import * + + +class ok_ssp_eligible(Variable): + value_type = bool + entity = Person + label = "Oklahoma State Supplemental Payment non-income eligibility" + definition_period = MONTH + reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" + defined_for = StateCode.OK + + def formula(person, period, parameters): + p = parameters(period).gov.ssa.ssi.eligibility.resources.limit + couple = person.marital_unit.nb_persons() == 2 + resources = person.marital_unit.sum( + person("ssi_countable_resources", period.this_year) + ) + immigration = person("immigration_status", period.this_year) + citizen = immigration == immigration.possible_values.CITIZEN + immigrant = person("is_ssi_qualified_noncitizen", period.this_year) + arrangement = person("ssi_federal_living_arrangement", period) + medical = arrangement == arrangement.possible_values.MEDICAL_TREATMENT_FACILITY + return ( + ~medical + & person("is_ssi_aged_blind_disabled", period.this_year) + & (resources <= where(couple, p.couple, p.individual)) + & (citizen | immigrant) + & ~person("ok_ssp_is_excluded", period) + & ~person("ok_ssp_is_in_tanf_unit", period) + & ~person("ssi_lives_in_medical_treatment_facility", period) + & ~person("is_incarcerated", period) + & person("takes_up_ssi_if_eligible", period) + ) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_has_essential_spouse.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_has_essential_spouse.py new file mode 100644 index 00000000000..a3a33de2f05 --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_has_essential_spouse.py @@ -0,0 +1,9 @@ +from policyengine_us.model_api import * + + +class ok_ssp_has_essential_spouse(Variable): + value_type = bool + entity = Person + label = "Oklahoma SSP recipient has an essential spouse" + definition_period = MONTH + reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_excluded.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_excluded.py new file mode 100644 index 00000000000..c1789c14f50 --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_excluded.py @@ -0,0 +1,10 @@ +from policyengine_us.model_api import * + + +class ok_ssp_is_excluded(Variable): + value_type = bool + entity = Person + label = "Oklahoma SSP exclusion applies" + definition_period = MONTH + reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" + documentation = "Public facility, psychiatric stay over 30 days, prohibited resource transfer, unmet benefit pursuit, or state-paid out-of-home child placement under 340:15-1-5(b)." diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py new file mode 100644 index 00000000000..415874fc8f3 --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py @@ -0,0 +1,10 @@ +from policyengine_us.model_api import * + + +class ok_ssp_is_in_tanf_unit(Variable): + value_type = bool + entity = Person + label = "Oklahoma TANF benefit includes this person" + definition_period = MONTH + reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" + documentation = "Verified inclusion of the person's needs in the TANF grant, rather than merely being the payee. Explicit input avoids a TANF/SSP eligibility cycle." diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_own_countable_income.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_own_countable_income.py new file mode 100644 index 00000000000..41a4a5b7fe1 --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_own_countable_income.py @@ -0,0 +1,14 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.ok.dhs.ssp._ok_ssp_income import ok_ssp_income + + +class ok_ssp_own_countable_income(Variable): + value_type = float + entity = Person + label = "Oklahoma SSP own countable income" + definition_period = MONTH + unit = USD + reference = "https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html" + + def formula(person, period, parameters): + return ok_ssp_income(person, period, parameters, False) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py new file mode 100644 index 00000000000..1dd0185e079 --- /dev/null +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class ok_ssp_spouse_child_allocation(Variable): + value_type = float + entity = Person + label = "Oklahoma SSP ineligible spouse allocation for dependent children" + definition_period = MONTH + unit = USD + reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" + documentation = "Default uses the family's non-SSI, non-TANF children. Override with the verified allocation for blended families or income allocated to children in TANF." + + def formula(person, period, parameters): + p = parameters(period).gov.states.ok.dhs.ssp + child = person("is_ssi_ineligible_child", period.this_year) + child_income = max_(person("ssi_earned_income", period), 0) + max_( + person("ssi_unearned_income", period), 0 + ) + no_tanf = ~person("ok_ssp_is_in_tanf_unit", period) + allocation = child * no_tanf * max_(p.child_allocation - child_income, 0) + return person.family.sum(allocation) diff --git a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py index 41ead044f46..48b12b72903 100644 --- a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py +++ b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py @@ -22,6 +22,7 @@ def formula(spm_unit, period, parameters): "ky_ssp", # Kentucky benefits "de_ssp", # Delaware benefits "fl_oss", + "ok_ssp", "ks_sspp", # Kansas benefits "hi_oss", "la_oss", # Louisiana benefits From dd6927c7a9c03f1a39a084c76834610f04660f60 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 13 Oct 2026 01:02:29 -0400 Subject: [PATCH 2/5] Normalize Oklahoma SSP parameter and test formatting --- .../household/household_state_benefits.yaml | 1 + .../hud/annual_income/sources/unearned.yaml | 1 + .../exclusions/child_support_share.yaml | 10 +- .../ok/dhs/ssp/blind_earned_exclusion.yaml | 12 +- .../states/ok/dhs/ssp/child_allocation.yaml | 9 +- .../dhs/ssp/ineligible_spouse_standard.yaml | 12 +- .../states/ok/dhs/ssp/maximum_payment.yaml | 9 +- .../gov/states/ok/dhs/ssp/integration.yaml | 229 ++++++------------ .../gov/states/ok/dhs/ssp/ok_ssp.yaml | 38 ++- 9 files changed, 136 insertions(+), 185 deletions(-) diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index 7cfab501ec3..efc2ba1e4a0 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -405,6 +405,7 @@ values: - nj_property_tax_relief # New York benefits - ny_child_care_subsidies + metadata: unit: list period: year diff --git a/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml b/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml index f87ca59ee1f..2e50267ebd2 100644 --- a/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml @@ -30,6 +30,7 @@ values: - disability_benefits - survivor_benefits - financial_assistance + metadata: unit: list period: year diff --git a/policyengine_us/parameters/gov/ssa/ssi/income/exclusions/child_support_share.yaml b/policyengine_us/parameters/gov/ssa/ssi/income/exclusions/child_support_share.yaml index a5f0fdb42a5..81c360b8302 100644 --- a/policyengine_us/parameters/gov/ssa/ssi/income/exclusions/child_support_share.yaml +++ b/policyengine_us/parameters/gov/ssa/ssi/income/exclusions/child_support_share.yaml @@ -1,10 +1,10 @@ -description: The Social Security Administration excludes this share of child support received - by an eligible child from SSI unearned income. +description: The Social Security Administration excludes this share of child support received by an eligible child from SSI unearned income. values: - '2026-01-01': 0.3333333333333333 + 2026-01-01: 0.3333333333333333 + metadata: unit: /1 label: SSI child support exclusion share reference: - - title: 20 CFR 416.1124(c)(11) - href: https://www.law.cornell.edu/cfr/text/20/416.1124 + - title: 20 CFR 416.1124(c)(11) + href: https://www.law.cornell.edu/cfr/text/20/416.1124 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml index 52242f0e28d..1c729ee574d 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml @@ -1,11 +1,11 @@ -description: Oklahoma excludes this additional amount of monthly earned income of a blind individual before - the standard earned income exclusions. +description: Oklahoma excludes this additional amount of monthly earned income of a blind individual before the standard earned income exclusions. values: - '2026-01-01': 500 + 2026-01-01: 500 + metadata: unit: currency-USD + period: month label: Oklahoma SSP blind earnings exclusion reference: - - title: OAC 317:35-5-42(g)(5) - href: https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html - period: month + - title: OAC 317:35-5-42(g)(5) + href: https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml index b778cba996c..7026ce114a5 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml @@ -1,10 +1,11 @@ description: Oklahoma uses this allocation for an ineligible child for its State Supplemental Payment. values: - '2026-01-01': 498 + 2026-01-01: 498 + metadata: unit: currency-USD + period: month label: Oklahoma SSP allocation for an ineligible child reference: - - title: Appendix C-1 Schedule VIII A, C - href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 - period: month + - title: Appendix C-1 Schedule VIII A, C + href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml index f329bed8360..b560568933b 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml @@ -1,11 +1,11 @@ -description: Oklahoma uses this need standard for an individual with an ineligible or essential spouse - for its State Supplemental Payment. +description: Oklahoma uses this need standard for an individual with an ineligible or essential spouse for its State Supplemental Payment. values: - '2026-01-01': 1532 + 2026-01-01: 1_532 + metadata: unit: currency-USD + period: month label: Oklahoma SSP need standard for an individual with an ineligible or essential spouse reference: - - title: Appendix C-1 Schedule VIII A, C - href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 - period: month + - title: Appendix C-1 Schedule VIII A, C + href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml index 1bb3c60c1ae..0cadfe8f426 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml @@ -1,10 +1,11 @@ description: Oklahoma uses this maximum payment per recipient for its State Supplemental Payment. values: - '2026-01-01': 40 + 2026-01-01: 40 + metadata: unit: currency-USD + period: month label: Oklahoma SSP maximum payment per recipient reference: - - title: Appendix C-1 Schedule VIII A, C - href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 - period: month + - title: Appendix C-1 Schedule VIII A, C + href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml index 7b20c6b1715..eae59c0c8c6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml @@ -3,296 +3,225 @@ absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - second: + person2: age: 65 marital_units: couple: - members: - - first - - second + members: [person1, person2] families: family: - members: - - first - - second + members: [person1, person2] households: hh: - members: - - first - - second + members: [person1, person2] state_code: OK output: - ok_ssp: - - 40 - - 40 + ok_ssp: [40, 40] + - name: Case 2, odd dollar couple grant. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - social_security_retirement: 18600 - second: + social_security_retirement: 18_600 + person2: age: 65 marital_units: couple: - members: - - first - - second + members: [person1, person2] families: family: - members: - - first - - second + members: [person1, person2] households: hh: - members: - - first - - second + members: [person1, person2] state_code: OK output: - ok_ssp: - - 20 - - 21 + ok_ssp: [20, 21] + - name: Case 3, ineligible spouse income leaves partial grant. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - second: + person2: age: 40 - social_security_retirement: 18480 + social_security_retirement: 18_480 marital_units: couple: - members: - - first - - second + members: [person1, person2] families: family: - members: - - first - - second + members: [person1, person2] households: hh: - members: - - first - - second + members: [person1, person2] state_code: OK output: - ok_ssp: - - 12 - - 0 + ok_ssp: [12, 0] + - name: Case 4, own income test is separate from combined test. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - social_security_retirement: 12720 - second: + social_security_retirement: 12_720 + person2: age: 40 marital_units: couple: - members: - - first - - second + members: [person1, person2] families: family: - members: - - first - - second + members: [person1, person2] households: hh: - members: - - first - - second + members: [person1, person2] state_code: OK output: - ok_ssp: - - 0 - - 0 + ok_ssp: [0, 0] + - name: Case 5, essential spouse uses combined test. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - social_security_retirement: 12720 + social_security_retirement: 12_720 ok_ssp_has_essential_spouse: true - second: + person2: age: 40 marital_units: couple: - members: - - first - - second + members: [person1, person2] families: family: - members: - - first - - second + members: [person1, person2] households: hh: - members: - - first - - second + members: [person1, person2] state_code: OK output: - ok_ssp: - - 40 - - 0 + ok_ssp: [40, 0] + - name: Case 6, spouse on TANF income is excluded. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - second: + person2: age: 40 - social_security_retirement: 60000 + social_security_retirement: 60_000 ok_ssp_is_in_tanf_unit: true marital_units: couple: - members: - - first - - second + members: [person1, person2] families: family: - members: - - first - - second + members: [person1, person2] households: hh: - members: - - first - - second + members: [person1, person2] state_code: OK output: - ok_ssp: - - 40 - - 0 + ok_ssp: [40, 0] + - name: Case 7, spouse child allocation supports recipient. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - second: + person2: age: 40 - social_security_retirement: 21600 - child: + social_security_retirement: 21_600 + person3: age: 10 marital_units: couple: - members: - - first - - second + members: [person1, person2] child: - members: - - child + members: [person3] families: family: - members: - - first - - second - - child + members: [person1, person2, person3] households: hh: - members: - - first - - second - - child + members: [person1, person2, person3] state_code: OK output: - ok_ssp: - - 40 - - 0 - - 0 + ok_ssp: [40, 0, 0] + - name: Case 8, child income reduces spouse allocation. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - second: + person2: age: 40 - social_security_retirement: 21600 - child: + social_security_retirement: 21_600 + person3: age: 10 - social_security_survivors: 4800 + social_security_survivors: 4_800 marital_units: couple: - members: - - first - - second + members: [person1, person2] child: - members: - - child + members: [person3] families: family: - members: - - first - - second - - child + members: [person1, person2, person3] households: hh: - members: - - first - - second - - child + members: [person1, person2, person3] state_code: OK output: - ok_ssp: - - 0 - - 0 - - 0 + ok_ssp: [0, 0, 0] + - name: Case 9, resources of ineligible spouse count. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 - second: + person2: age: 40 - ssi_countable_resources: 3001 + ssi_countable_resources: 3_001 marital_units: couple: - members: - - first - - second + members: [person1, person2] families: family: - members: - - first - - second + members: [person1, person2] households: hh: - members: - - first - - second + members: [person1, person2] state_code: OK output: - ok_ssp: - - 0 - - 0 + ok_ssp: [0, 0] + - name: Case 10, annual supplement and SNAP income. - period: '2026' + period: 2026 absolute_error_margin: 0.01 input: state_code: OK age: 65 output: ok_ssp: 480 - snap_unearned_income: 12408 - hud_unearned_income: 12408 + snap_unearned_income: 12_408 + hud_unearned_income: 12_408 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml index f2c407b8aee..c58f0dea691 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml @@ -6,6 +6,7 @@ age: 65 output: ok_ssp: 40 + - name: Case 2, small cash income gets maximum supplement. period: 2026-01 absolute_error_margin: 0.01 @@ -15,89 +16,99 @@ social_security_retirement: 240 output: ok_ssp: 40 + - name: Case 3, state only payment above SSI limit. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - social_security_retirement: 12240 + social_security_retirement: 12_240 output: ok_ssp: 34 + - name: Case 4, income at state need standard. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - social_security_retirement: 12648 + social_security_retirement: 12_648 output: ok_ssp: 0 + - name: Case 5, earnings without special blind exclusion. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - employment_income: 30000 + employment_income: 30_000 output: ok_ssp: 0 + - name: Case 6, extra five hundred dollar exclusion for blind earnings. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - employment_income: 30000 + employment_income: 30_000 is_blind: true output: ok_ssp: 40 + - name: Case 7, income sources round before exclusions. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - social_security_retirement: 12245.88 + social_security_retirement: 12_245.88 output: ok_ssp: 34 + - name: Case 8, half dollar source rounds upward. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - social_security_retirement: 12246 + social_security_retirement: 12_246 output: ok_ssp: 33 + - name: Case 9, interest from resources is excluded. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - social_security_retirement: 12240 - interest_income: 1200 + social_security_retirement: 12_240 + interest_income: 1_200 output: ok_ssp: 34 + - name: Case 10, countable resources at limit. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - ssi_countable_resources: 2000 + ssi_countable_resources: 2_000 output: ok_ssp: 40 + - name: Case 11, excess countable resources. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - ssi_countable_resources: 2001 + ssi_countable_resources: 2_001 output: ok_ssp: 0 + - name: Case 12, blind child is eligible. period: 2026-01 absolute_error_margin: 0.01 @@ -107,6 +118,7 @@ is_blind: true output: ok_ssp: 40 + - name: Case 13, nonresident. period: 2026-01 absolute_error_margin: 0.01 @@ -115,6 +127,7 @@ age: 65 output: ok_ssp: 0 + - name: Case 14, not aged blind or disabled. period: 2026-01 absolute_error_margin: 0.01 @@ -123,6 +136,7 @@ age: 40 output: ok_ssp: 0 + - name: Case 15, opt out. period: 2026-01 absolute_error_margin: 0.01 @@ -132,6 +146,7 @@ takes_up_ssi_if_eligible: false output: ok_ssp: 0 + - name: Case 16, TANF member cannot also get SSP. period: 2026-01 absolute_error_margin: 0.01 @@ -141,6 +156,7 @@ ok_ssp_is_in_tanf_unit: true output: ok_ssp: 0 + - name: Case 17, excluded facility or sanction. period: 2026-01 absolute_error_margin: 0.01 @@ -150,6 +166,7 @@ ok_ssp_is_excluded: true output: ok_ssp: 0 + - name: Case 18, institutional SSI is outside optional community program. period: 2026-01 absolute_error_margin: 0.01 @@ -159,6 +176,7 @@ ssi_federal_living_arrangement: MEDICAL_TREATMENT_FACILITY output: ok_ssp: 0 + - name: Case 19, VTR does not increase the forty dollar maximum. period: 2026-01 absolute_error_margin: 0.01 From e4b706d42184b77e5e5e59a2656adeb2a4605e1e Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 13 Oct 2026 02:52:40 -0400 Subject: [PATCH 3/5] Fix issues from review: Oklahoma SSP eligibility, couple income test and inputs - Build income totals without builtin sum() over entity calls (code-health rule) - Gate on is_ssi_eligible so parental resource deeming applies - Count the one-third reduction value once in the couple test - Treat only a co-resident spouse who is not aged, blind or disabled as an ineligible spouse - Drop the TANF-unit and essential-spouse inputs; move excluded income sources to a parameter - Registry placement and comments, SNAP reference title, shorter programs.yaml note, Appendix C-1 page locators - Realistic households and new eligibility, couple and child-support cases Co-Authored-By: Claude Opus 5.5 --- .../household/household_state_benefits.yaml | 8 +- .../states/ok/dhs/ssp/child_allocation.yaml | 4 +- .../ok/dhs/ssp/excluded_unearned_sources.yaml | 13 + .../dhs/ssp/ineligible_spouse_standard.yaml | 2 +- .../states/ok/dhs/ssp/maximum_payment.yaml | 2 +- .../usda/snap/income/sources/unearned.yaml | 4 +- policyengine_us/programs.yaml | 8 +- .../gov/states/ok/dhs/ssp/integration.yaml | 247 +++++++++++++++++- .../gov/states/ok/dhs/ssp/ok_ssp.yaml | 54 +++- .../gov/states/ok/dhs/ssp/_ok_ssp_income.py | 29 +- .../variables/gov/states/ok/dhs/ssp/ok_ssp.py | 28 +- .../ok/dhs/ssp/ok_ssp_countable_income.py | 2 +- .../gov/states/ok/dhs/ssp/ok_ssp_eligible.py | 27 +- .../ok/dhs/ssp/ok_ssp_has_essential_spouse.py | 9 - .../ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py | 10 - .../ok/dhs/ssp/ok_ssp_own_countable_income.py | 2 +- .../dhs/ssp/ok_ssp_spouse_child_allocation.py | 5 +- .../income/spm_unit/spm_unit_benefits.py | 2 +- 18 files changed, 351 insertions(+), 105 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/ok/dhs/ssp/excluded_unearned_sources.yaml delete mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_has_essential_spouse.py delete mode 100644 policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index efc2ba1e4a0..fdfc5c6b269 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -63,6 +63,7 @@ values: - nv_child_care_subsidies # Oklahoma benefits - ok_child_care_subsidies + - ok_ssp # South Carolina benefits - sc_child_care_subsidies # New Mexico benefits @@ -78,7 +79,6 @@ values: # DC benefits - dc_ossp # Kansas benefits - - ok_ssp - ks_sspp # Hawaii benefits - hi_oss @@ -158,6 +158,7 @@ values: - nv_child_care_subsidies # Oklahoma benefits - ok_child_care_subsidies + - ok_ssp # New Mexico benefits - nm_ssi_state_supplement # South Carolina benefits @@ -177,7 +178,6 @@ values: # DC benefits - dc_ossp # Kansas benefits - - ok_ssp - ks_sspp # Hawaii benefits - hi_oss @@ -261,6 +261,7 @@ values: - nv_child_care_subsidies # Oklahoma benefits - ok_child_care_subsidies + - ok_ssp # New Mexico benefits - nm_ssi_state_supplement # South Carolina benefits @@ -281,7 +282,6 @@ values: - dc_ossp - dc_child_care_subsidies # Kansas benefits - - ok_ssp - ks_sspp # Hawaii benefits - hi_oss @@ -365,6 +365,7 @@ values: - nv_child_care_subsidies # Oklahoma benefits - ok_child_care_subsidies + - ok_ssp # New Mexico benefits - nm_ssi_state_supplement # South Carolina benefits @@ -385,7 +386,6 @@ values: - dc_ossp - dc_child_care_subsidies # Kansas benefits - - ok_ssp - ks_sspp # Hawaii benefits - hi_oss diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml index 7026ce114a5..1e2b0138d1f 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml @@ -7,5 +7,5 @@ metadata: period: month label: Oklahoma SSP allocation for an ineligible child reference: - - title: Appendix C-1 Schedule VIII A, C - href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 + - title: Appendix C-1 Schedule VIII.C + href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=4 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/excluded_unearned_sources.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/excluded_unearned_sources.yaml new file mode 100644 index 00000000000..385449c8e85 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/excluded_unearned_sources.yaml @@ -0,0 +1,13 @@ +description: Oklahoma excludes these unearned income sources from countable income under the State Supplemental Payment program. +values: + 2026-01-01: + - dividend_income + - interest_income + +metadata: + unit: list + period: year + label: Oklahoma SSP excluded unearned income sources + reference: + - title: OAC 317:35-5-42(f)(22) + href: https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml index b560568933b..a085c1b7e22 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml @@ -7,5 +7,5 @@ metadata: period: month label: Oklahoma SSP need standard for an individual with an ineligible or essential spouse reference: - - title: Appendix C-1 Schedule VIII A, C + - title: Appendix C-1 Schedule VIII.A href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml index 0cadfe8f426..6ae2ecbad7f 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml @@ -7,5 +7,5 @@ metadata: period: month label: Oklahoma SSP maximum payment per recipient reference: - - title: Appendix C-1 Schedule VIII A, C + - title: Appendix C-1 Schedule VIII.A href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 diff --git a/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml index de223268792..77410bf1e28 100644 --- a/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml @@ -36,7 +36,6 @@ values: - hi_oss - id_aabd - in_ssp - - ok_ssp - ks_sspp - ky_ssp - la_oss @@ -48,6 +47,7 @@ values: - mo_ssp - ne_aabd - nm_ssi_state_supplement + - ok_ssp - sc_ssi_state_supplement - tx_ssi_state_supplement - wa_ssp @@ -96,7 +96,7 @@ values: metadata: reference: - - title: 7 CFR 273.9(b)(2) and (c)(1), (5) + - title: 7 CFR 273.9(b)(2) and (c)(1) href: https://www.law.cornell.edu/cfr/text/7/273.9 - title: POMS SI 01401.001 General Information about State Supplementation href: https://secure.ssa.gov/poms.nsf/lnx/0501401001 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 32c857cc063..d80835bba14 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1097,11 +1097,9 @@ programs: variable: ok_ssp parameter_prefix: gov.states.ok.dhs.ssp verified_years: '2026' - notes: Optional community SSP, including the distinct ineligible/essential spouse tests, - blind earnings exclusion and child allocations. Procedural exclusions and individual - TANF membership require inputs. Override countable income for specialized work expenses, - sponsor deeming or state-specific parental deeming; grandfathered institutional payments - are excluded. + notes: Models the 2026 community supplement for individuals, couples and individuals with + an ineligible spouse. Essential spouses, persons in a TANF case and grandfathered + institutional payments are not modeled. - id: social_security name: Social Security diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml index eae59c0c8c6..af54d9bca1f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml @@ -51,7 +51,7 @@ person1: age: 65 person2: - age: 40 + age: 63 social_security_retirement: 18_480 marital_units: couple: @@ -89,17 +89,18 @@ output: ok_ssp: [0, 0] -- name: Case 5, essential spouse uses combined test. +- name: Case 5, aged spouse who declines SSI leaves the claimant on the individual test. period: 2026-01 absolute_error_margin: 0.01 input: people: person1: - age: 65 - social_security_retirement: 12_720 - ok_ssp_has_essential_spouse: true + age: 68 + social_security_retirement: 6_000 person2: - age: 40 + age: 66 + social_security_retirement: 18_000 + takes_up_ssi_if_eligible: false marital_units: couple: members: [person1, person2] @@ -111,19 +112,26 @@ members: [person1, person2] state_code: OK output: + # person2 does not take up, so only person1 is SSP-eligible. + ok_ssp_eligible: [true, false] + # An aged spouse is not an ineligible spouse, so person1 is tested alone. + # Federal couple SSI: (500 + 1_500 - 20) / 2 = 990 > 745.5, so 0. + # Own income 500 - 20 = 480 + 0 SSI; min(1_034 - 480, 40) = 40. ok_ssp: [40, 0] -- name: Case 6, spouse on TANF income is excluded. +- name: Case 6, disabled spouse barred from SSP leaves the claimant on the individual test. period: 2026-01 absolute_error_margin: 0.01 input: people: person1: - age: 65 + age: 68 + social_security_retirement: 6_000 person2: - age: 40 - social_security_retirement: 60_000 - ok_ssp_is_in_tanf_unit: true + age: 55 + meets_ssi_disability_criteria: true + social_security_disability: 18_000 + ok_ssp_is_excluded: true marital_units: couple: members: [person1, person2] @@ -135,6 +143,11 @@ members: [person1, person2] state_code: OK output: + # person2 is disabled but excluded, so only person1 is SSP-eligible. + ok_ssp_eligible: [true, false] + # A disabled spouse is not an ineligible spouse; the 1_500 SSDI is not + # combined. Federal couple SSI: (500 + 1_500 - 20) / 2 = 990 > 745.5, so 0. + # Own income 480 + 0 SSI; min(1_034 - 480, 40) = 40. ok_ssp: [40, 0] - name: Case 7, spouse child allocation supports recipient. @@ -146,7 +159,7 @@ age: 65 person2: age: 40 - social_security_retirement: 21_600 + employment_income: 41_600 person3: age: 10 marital_units: @@ -162,6 +175,10 @@ members: [person1, person2, person3] state_code: OK output: + # Federal: 41_600 - 5_964 child allocation = 35_636 deemed earnings; + # 1_491 - (2_969.67 - 85) / 2 = 48.67 SSI. + # Combined: 3_466.67 rounds to 3_467; (3_467 - 498 - 85) / 2 = 1_442 + # + 48.67 = 1_490.67 < 1_532; min(41.33, 40) = 40. ok_ssp: [40, 0, 0] - name: Case 8, child income reduces spouse allocation. @@ -173,7 +190,7 @@ age: 65 person2: age: 40 - social_security_retirement: 21_600 + employment_income: 41_600 person3: age: 10 social_security_survivors: 4_800 @@ -190,6 +207,9 @@ members: [person1, person2, person3] state_code: OK output: + # Federal: 41_600 - (5_964 - 4_800) = 40_436 deemed earnings; + # (3_369.67 - 85) / 2 = 1_642.33 > 1_491, so SSI is 0. + # Allocation 498 - 400 = 98; (3_467 - 98 - 85) / 2 = 1_642 >= 1_532. ok_ssp: [0, 0, 0] - name: Case 9, resources of ineligible spouse count. @@ -225,3 +245,204 @@ ok_ssp: 480 snap_unearned_income: 12_408 hud_unearned_income: 12_408 + +- name: Case 11, parents' savings over the deeming allowance bar a blind child. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + person_id: 1 + employment_income: 45_000 + bank_account_assets: 9_000 + person2: + age: 38 + person_id: 2 + person3: + age: 8 + person_id: 3 + parent_1_id: 1 + parent_2_id: 2 + is_blind: true + marital_units: + parents: + members: [person1, person2] + child: + members: [person3] + families: + family: + members: [person1, person2, person3] + households: + hh: + members: [person1, person2, person3] + state_code: OK + output: + # 9_000 parental savings - 3_000 two-parent allowance = 6_000 deemed. + ssi_resources_deemed_from_ineligible_parent: [0, 0, 6_000] + # Child: 0 own + 6_000 deemed > 2_000 limit; parents are not aged, + # blind or disabled. + is_ssi_eligible: [false, false, false] + ok_ssp_eligible: [false, false, false] + # Father: (3_750 - 85) / 2 = 1_832.5. Child: deemed parental income + # (3_750 - 85) / 2 - 2 * 745.5 = 341.5, less 20 = 321.5, with no SSI. + ok_ssp_own_countable_income: [1_832.5, 0, 321.5] + # 321.5 < 1_034, so only the resource test bars the payment. + ok_ssp: [0, 0, 0] + +- name: Case 12, one-third value counts once for an SSI husband with an ineligible wife in their daughter's home. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + person2: + age: 62 + social_security_retirement: 4_800 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + marital_units: + couple: + members: [person1, person2] + families: + family: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OK + output: + # Wife's 400 is below the 1_491 - 994 = 497 deeming threshold. + # Husband: 994 * (1 - 1/3) = 662.67 reduced federal rate. + ssi: [662.67, 0] + ok_ssp_eligible: [true, false] + # Husband: 662.67 SSI + 994 / 3 = 331.33 one-third value = 994. + # Wife: 400 - 20 = 380 + 331.33 = 711.33 (not used). + ok_ssp_own_countable_income: [994, 711.33] + # 380 + 662.67 + 331.33 for the husband only = 1_374. + ok_ssp_countable_income: [1_374, 1_374] + # Own 994 < 1_034; min(1_532 - 1_374, 40) = 40. + ok_ssp: [40, 0] + +- name: Case 13, aged spouse in a medical facility leaves the community spouse on the individual test. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + social_security_retirement: 7_200 + person2: + age: 66 + social_security_retirement: 7_200 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + marital_units: + couple: + members: [person1, person2] + families: + family: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OK + output: + # Separate computation: 994 - (600 - 20) = 414; facility rate 30 - 580 < 0. + ssi: [414, 0] + ok_ssp_eligible: [true, false] + # Community spouse: 580 + 414 = 994. Facility spouse: 600 - 20 = 580. + ok_ssp_own_countable_income: [994, 580] + # 1_200 - 20 + 414 = 1_594 (not used). + ok_ssp_countable_income: [1_594, 1_594] + # min(1_034 - 994, 40) = 40. + ok_ssp: [40, 0] + +- name: Case 14, one-third of child support is excluded for a disabled child. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 32 + person_id: 1 + employment_income: 31_200 + person2: + age: 9 + person_id: 2 + parent_1_id: 1 + meets_ssi_disability_criteria: true + child_support_received: 4_800 + marital_units: + parent: + members: [person1] + child: + members: [person2] + families: + family: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OK + output: + # (2_600 - 85) / 2 = 1_257.5 - 994 parent allocation = 263.5. + ssi_unearned_income_deemed_from_ineligible_parent: [0, 263.5] + # Federal SSI counts all child support: 994 - (400 + 263.5 - 20) = 350.5. + ssi: [0, 350.5] + ok_ssp_eligible: [false, true] + # Mother: (2_600 - 85) / 2 = 1_257.5. + # Child: 400 - 400 / 3 = 266.67 + 263.5 - 20 = 510.17 + 350.5 = 860.67. + ok_ssp_own_countable_income: [1_257.5, 860.67] + ok_ssp_countable_income: [1_257.5, 860.67] + # min(1_034 - 860.67, 40) = 40. + ok_ssp: [0, 40] + +- name: Case 15, children's allocation is summed and reduced by a teen's earnings. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + meets_ssi_disability_criteria: true + person2: + age: 43 + employment_income: 41_600 + person3: + age: 16 + employment_income: 3_600 + person4: + age: 9 + marital_units: + couple: + members: [person1, person2] + teen: + members: [person3] + child: + members: [person4] + families: + family: + members: [person1, person2, person3, person4] + households: + hh: + members: [person1, person2, person3, person4] + state_code: OK + output: + # (498 - 300) + 498 = 696. + ok_ssp_spouse_child_allocation: [696, 696, 696, 696] + # Federal: 41_600 - (5_964 - 3_600) - 5_964 = 33_272 deemed earnings; + # (2_772.67 - 85) / 2 = 1_343.83; 1_491 - 1_343.83 = 147.17. + ssi: [147.17, 0, 0, 0] + ok_ssp_eligible: [true, false, false, false] + # Claimant: 147.17 SSI. Wife: (3_467 - 85) / 2 = 1_691. + # Teen: (300 - 85) / 2 = 107.5. + ok_ssp_own_countable_income: [147.17, 1_691, 107.5, 0] + # Combined: (3_467 - 696 - 85) / 2 = 1_343 + 147.17 = 1_490.17; + # min(1_532 - 1_490.17, 40) = 40. + ok_ssp: [40, 0, 0, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml index c58f0dea691..fccc9b78416 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml @@ -113,11 +113,30 @@ period: 2026-01 absolute_error_margin: 0.01 input: - state_code: OK - age: 10 - is_blind: true - output: - ok_ssp: 40 + people: + person1: + age: 34 + person_id: 1 + employment_income: 15_080 + person2: + age: 10 + person_id: 2 + parent_1_id: 1 + is_blind: true + marital_units: + parent: + members: [person1] + child: + members: [person2] + households: + hh: + members: [person1, person2] + state_code: OK + output: + # Parent works full time at 7.25 an hour: (1_256.67 - 85) / 2 = 585.83 + # is below the 994 parent allocation, so nothing is deemed. + # Child SSI 994; min(1_034 - 994, 40) = 40. + ok_ssp: [0, 40] - name: Case 13, nonresident. period: 2026-01 @@ -147,14 +166,15 @@ output: ok_ssp: 0 -- name: Case 16, TANF member cannot also get SSP. +- name: Case 16, incarcerated person cannot get SSP. period: 2026-01 absolute_error_margin: 0.01 input: state_code: OK age: 65 - ok_ssp_is_in_tanf_unit: true + is_incarcerated: true output: + # Incarceration fails ok_ssp_eligible. ok_ssp: 0 - name: Case 17, excluded facility or sanction. @@ -173,8 +193,10 @@ input: state_code: OK age: 65 - ssi_federal_living_arrangement: MEDICAL_TREATMENT_FACILITY + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true output: + # Living in a medical treatment facility fails ok_ssp_eligible. ok_ssp: 0 - name: Case 19, VTR does not increase the forty dollar maximum. @@ -186,3 +208,19 @@ ssi_federal_living_arrangement: ANOTHER_PERSONS_HOUSEHOLD output: ok_ssp: 40 + +- name: Case 20, facility living arrangement alone bars SSP. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 65 + ssi_federal_living_arrangement: MEDICAL_TREATMENT_FACILITY + output: + # Aged, 0 resources <= 2_000, citizen: otherwise SSI-eligible. + is_ssi_eligible: true + # The facility booleans stay false, but the arrangement is + # MEDICAL_TREATMENT_FACILITY, so in_facility is true. + ok_ssp_eligible: false + # defined_for ok_ssp_eligible, so 0. + ok_ssp: 0 diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py index a715bf21c2c..4c287f6467a 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py @@ -4,19 +4,17 @@ ) -def ok_ssp_income(person, period, parameters, combined): +def ok_ssp_income(person, period, parameters, *, combined): p = parameters(period).gov.states.ok.dhs.ssp ssi = parameters(period).gov.ssa.ssi # 317:35-5-42(i) rounds each monthly source before applying exclusions. - earned = sum( - np.floor(max_(person(source, period), 0) + 0.5) - for source in ssi.income.sources.earned - ) - unearned = sum( - np.floor(max_(person(source, period), 0) + 0.5) - for source in ssi.income.sources.unearned - if source not in ("interest_income", "dividend_income") - ) + earned = 0 + for source in ssi.income.sources.earned: + earned = earned + np.floor(max_(person(source, period), 0) + 0.5) + unearned = 0 + for source in ssi.income.sources.unearned: + if source not in p.excluded_unearned_sources: + unearned = unearned + np.floor(max_(person(source, period), 0) + 0.5) child = person("is_child", period.this_year) unearned = max_( unearned @@ -51,11 +49,12 @@ def ok_ssp_income(person, period, parameters, combined): allocation = ~eligible * person("ok_ssp_spouse_child_allocation", period) earned = max_(earned - max_(allocation - unearned, 0), 0) unearned = max_(unearned - allocation, 0) - included = ~person("ok_ssp_is_in_tanf_unit", period) - earned = person.marital_unit.sum(earned * included) - unearned = person.marital_unit.sum(unearned * included) - benefit = person.marital_unit.sum(benefit * included) - shelter = person.marital_unit.sum(shelter * included) + earned = person.marital_unit.sum(earned) + unearned = person.marital_unit.sum(unearned) + benefit = person.marital_unit.sum(benefit) + # 317:35-5-42(b)(8)(A) counts the one-third value once, for the + # individual or the eligible couple, never for an ineligible spouse. + shelter = person.marital_unit.sum(shelter * eligible) countable = ( _apply_ssi_exclusions( earned * MONTHS_IN_YEAR, unearned * MONTHS_IN_YEAR, parameters, period diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py index df32798ac6b..e8c82819588 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py @@ -17,27 +17,31 @@ def formula(person, period, parameters): couple_standard = ssi.couple + 2 * p.maximum_payment recipients = person.marital_unit.sum(person("ok_ssp_eligible", period)) joint = recipients == 2 - has_spouse = person.marital_unit.nb_persons() == 2 - spouse_tanf = ( - person.marital_unit.sum(person("ok_ssp_is_in_tanf_unit", period)) > 0 + # OAC 340:15-1-2: an ineligible spouse lives with the individual and is + # not aged, blind or disabled. With any other spouse the claimant is an + # individual. Essential spouses (in the case before 1974) are not + # modeled separately. + abd = person("is_ssi_aged_blind_disabled", period.this_year) + arrangement = person("ssi_federal_living_arrangement", period) + in_facility = person("ssi_lives_in_medical_treatment_facility", period) | ( + arrangement == arrangement.possible_values.MEDICAL_TREATMENT_FACILITY ) - with_spouse = has_spouse & ~spouse_tanf + apart = in_facility | person("is_incarcerated", period) + ineligible_spouse = person.marital_unit.sum(~abd & ~apart) > 0 own = person("ok_ssp_own_countable_income", period) combined = person("ok_ssp_countable_income", period) standard = where( joint, couple_standard, - where(with_spouse, p.ineligible_spouse_standard, individual_standard), + where(ineligible_spouse, p.ineligible_spouse_standard, individual_standard), ) - income = where(with_spouse, combined, own) + income = where(joint | ineligible_spouse, combined, own) gap = max_(standard - income, 0) - # Assign an indivisible dollar to the second member of the couple. + # OAC 340:15-1-5(c)(1)(B) and Appendix C-1 Schedule VIII.A give one + # spouse the odd dollar without naming which, so person-level amounts + # follow member order while the couple total does not. first_share = np.floor(gap / 2) first = person.marital_unit.reference_entity.members_position == 0 payment = where(joint, where(first, first_share, gap - first_share), gap) - own_test = ( - joint - | person("ok_ssp_has_essential_spouse", period) - | (own < individual_standard) - ) + own_test = joint | (own < individual_standard) return own_test * min_(payment, p.maximum_payment) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_countable_income.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_countable_income.py index 6c7857ccb64..c59ad6abc21 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_countable_income.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_countable_income.py @@ -11,4 +11,4 @@ class ok_ssp_countable_income(Variable): reference = "https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html" def formula(person, period, parameters): - return ok_ssp_income(person, period, parameters, True) + return ok_ssp_income(person, period, parameters, combined=True) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_eligible.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_eligible.py index e4710de54c6..8339f1183cc 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_eligible.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_eligible.py @@ -10,24 +10,17 @@ class ok_ssp_eligible(Variable): defined_for = StateCode.OK def formula(person, period, parameters): - p = parameters(period).gov.ssa.ssi.eligibility.resources.limit - couple = person.marital_unit.nb_persons() == 2 - resources = person.marital_unit.sum( - person("ssi_countable_resources", period.this_year) - ) - immigration = person("immigration_status", period.this_year) - citizen = immigration == immigration.possible_values.CITIZEN - immigrant = person("is_ssi_qualified_noncitizen", period.this_year) arrangement = person("ssi_federal_living_arrangement", period) - medical = arrangement == arrangement.possible_values.MEDICAL_TREATMENT_FACILITY + in_facility = person("ssi_lives_in_medical_treatment_facility", period) | ( + arrangement == arrangement.possible_values.MEDICAL_TREATMENT_FACILITY + ) + # NOTE: OAC 340:15-1-5(d) bars a person whose needs are in a TANF + # case, and (c)(4)-(5) change the spouse income test when the spouse + # or children are in one. Neither is modeled, to avoid a TANF/SSP cycle. return ( - ~medical - & person("is_ssi_aged_blind_disabled", period.this_year) - & (resources <= where(couple, p.couple, p.individual)) - & (citizen | immigrant) - & ~person("ok_ssp_is_excluded", period) - & ~person("ok_ssp_is_in_tanf_unit", period) - & ~person("ssi_lives_in_medical_treatment_facility", period) + person("is_ssi_eligible", period) + & ~in_facility & ~person("is_incarcerated", period) - & person("takes_up_ssi_if_eligible", period) + & ~person("ok_ssp_is_excluded", period) + & person("takes_up_ssi_if_eligible", period.this_year) ) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_has_essential_spouse.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_has_essential_spouse.py deleted file mode 100644 index a3a33de2f05..00000000000 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_has_essential_spouse.py +++ /dev/null @@ -1,9 +0,0 @@ -from policyengine_us.model_api import * - - -class ok_ssp_has_essential_spouse(Variable): - value_type = bool - entity = Person - label = "Oklahoma SSP recipient has an essential spouse" - definition_period = MONTH - reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py deleted file mode 100644 index 415874fc8f3..00000000000 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_in_tanf_unit.py +++ /dev/null @@ -1,10 +0,0 @@ -from policyengine_us.model_api import * - - -class ok_ssp_is_in_tanf_unit(Variable): - value_type = bool - entity = Person - label = "Oklahoma TANF benefit includes this person" - definition_period = MONTH - reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" - documentation = "Verified inclusion of the person's needs in the TANF grant, rather than merely being the payee. Explicit input avoids a TANF/SSP eligibility cycle." diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_own_countable_income.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_own_countable_income.py index 41a4a5b7fe1..75ef3c3852d 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_own_countable_income.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_own_countable_income.py @@ -11,4 +11,4 @@ class ok_ssp_own_countable_income(Variable): reference = "https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html" def formula(person, period, parameters): - return ok_ssp_income(person, period, parameters, False) + return ok_ssp_income(person, period, parameters, combined=False) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py index 1dd0185e079..c8424beda1e 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py @@ -8,7 +8,7 @@ class ok_ssp_spouse_child_allocation(Variable): definition_period = MONTH unit = USD reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" - documentation = "Default uses the family's non-SSI, non-TANF children. Override with the verified allocation for blended families or income allocated to children in TANF." + documentation = "Default uses the family's non-SSI children. Override with the verified allocation for blended families or children in a TANF case." def formula(person, period, parameters): p = parameters(period).gov.states.ok.dhs.ssp @@ -16,6 +16,5 @@ def formula(person, period, parameters): child_income = max_(person("ssi_earned_income", period), 0) + max_( person("ssi_unearned_income", period), 0 ) - no_tanf = ~person("ok_ssp_is_in_tanf_unit", period) - allocation = child * no_tanf * max_(p.child_allocation - child_income, 0) + allocation = child * max_(p.child_allocation - child_income, 0) return person.family.sum(allocation) diff --git a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py index 48b12b72903..4e08e5674b5 100644 --- a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py +++ b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py @@ -22,7 +22,7 @@ def formula(spm_unit, period, parameters): "ky_ssp", # Kentucky benefits "de_ssp", # Delaware benefits "fl_oss", - "ok_ssp", + "ok_ssp", # Oklahoma benefits "ks_sspp", # Kansas benefits "hi_oss", "la_oss", # Louisiana benefits From 50bb3a7d94282dffa2e4a7074a5abaebce81c3ba Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 15 Oct 2026 16:05:40 -0400 Subject: [PATCH 4/5] Address review: Oklahoma SSP spouse income, 538-H income and standards - Count Oklahoma SSP in gross household income for Forms 538-H and 538-S. - Exclude an ineligible spouse's in-kind support and SSI from the couple test, and count an SSI couple's presumed maximum value once. - Treat any spouse living together who is not SSP-eligible as an ineligible spouse (OAC 317:35-5-42(k)(1)). - Uprate the ineligible-spouse standard and child allocation with the SSI cost-of-living adjustment (OAC 340:15-1-4(9)). - Update descriptions, references and documentation, and add tests. Co-Authored-By: Claude Opus 5.5 --- .../ok/dhs/ssp/blind_earned_exclusion.yaml | 2 +- .../states/ok/dhs/ssp/child_allocation.yaml | 14 ++- .../dhs/ssp/ineligible_spouse_standard.yaml | 14 ++- .../states/ok/dhs/ssp/maximum_payment.yaml | 4 +- .../income/credits/gross_income_sources.yaml | 4 +- .../gov/states/ok/dhs/ssp/integration.yaml | 110 ++++++++++++++++-- .../states/ok/tax/income/ok_gross_income.yaml | 13 +++ .../gov/states/ok/dhs/ssp/_ok_ssp_income.py | 27 ++++- .../variables/gov/states/ok/dhs/ssp/ok_ssp.py | 32 +++-- .../states/ok/dhs/ssp/ok_ssp_is_excluded.py | 9 +- .../dhs/ssp/ok_ssp_spouse_child_allocation.py | 2 +- 11 files changed, 197 insertions(+), 34 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml index 1c729ee574d..e1dc7548a81 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/blind_earned_exclusion.yaml @@ -1,4 +1,4 @@ -description: Oklahoma excludes this additional amount of monthly earned income of a blind individual before the standard earned income exclusions. +description: Oklahoma excludes this amount of a blind individual's monthly earned income under the State Supplemental Payment program. values: 2026-01-01: 500 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml index 1e2b0138d1f..de0da95fd3a 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/child_allocation.yaml @@ -1,4 +1,4 @@ -description: Oklahoma uses this allocation for an ineligible child for its State Supplemental Payment. +description: Oklahoma allocates this monthly amount for each ineligible child under the State Supplemental Payment program. values: 2026-01-01: 498 @@ -6,6 +6,16 @@ metadata: unit: currency-USD period: month label: Oklahoma SSP allocation for an ineligible child + # OAC 340:15-1-4(9)(A)-(B) bases the SSP standards on the FBR and passes + # along the SSI cost-of-living adjustment; the next Appendix C-1 edition + # replaces the projection. + uprating: + parameter: gov.ssa.uprating + rounding: + type: nearest + interval: 1 reference: - - title: Appendix C-1 Schedule VIII.C + - title: OKDHS Appendix C-1 (7/1/2026), Schedule VIII.C href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=4 + - title: OAC 340:15-1-4(9) + href: https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-4 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml index a085c1b7e22..07fd44ea1e0 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/ineligible_spouse_standard.yaml @@ -1,4 +1,4 @@ -description: Oklahoma uses this need standard for an individual with an ineligible or essential spouse for its State Supplemental Payment. +description: Oklahoma sets this amount as the monthly need standard for an individual with an ineligible or essential spouse under the State Supplemental Payment program. values: 2026-01-01: 1_532 @@ -6,6 +6,16 @@ metadata: unit: currency-USD period: month label: Oklahoma SSP need standard for an individual with an ineligible or essential spouse + # OAC 340:15-1-4(9)(A)-(B) bases the SSP standards on the FBR and passes + # along the SSI cost-of-living adjustment; the next Appendix C-1 edition + # replaces the projection. + uprating: + parameter: gov.ssa.uprating + rounding: + type: nearest + interval: 1 reference: - - title: Appendix C-1 Schedule VIII.A + - title: OKDHS Appendix C-1 (7/1/2026), Schedule VIII.A href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 + - title: OAC 340:15-1-4(9) + href: https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-4 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml index 6ae2ecbad7f..526523d44b1 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ssp/maximum_payment.yaml @@ -1,4 +1,4 @@ -description: Oklahoma uses this maximum payment per recipient for its State Supplemental Payment. +description: Oklahoma limits each recipient's monthly payment to this amount under the State Supplemental Payment program. values: 2026-01-01: 40 @@ -7,5 +7,5 @@ metadata: period: month label: Oklahoma SSP maximum payment per recipient reference: - - title: Appendix C-1 Schedule VIII.A + - title: OKDHS Appendix C-1 (7/1/2026), Schedule VIII.A href: https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=3 diff --git a/policyengine_us/parameters/gov/states/ok/tax/income/credits/gross_income_sources.yaml b/policyengine_us/parameters/gov/states/ok/tax/income/credits/gross_income_sources.yaml index 856f98bac24..ba7ea6691bc 100644 --- a/policyengine_us/parameters/gov/states/ok/tax/income/credits/gross_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ok/tax/income/credits/gross_income_sources.yaml @@ -25,7 +25,7 @@ values: - illicit_income - miscellaneous_income - ssi - # - ok_state_supplement + - ok_ssp - general_assistance - eitc @@ -42,7 +42,7 @@ metadata: href: https://oklahoma.gov/content/dam/ok/en/tax/documents/forms/individuals/past-year/2024/511-Pkt-2024.pdf - title: 2025 Form 511 packet, Form 538-S, Part 3 (gross income lines 9 to 14) href: https://oklahoma.gov/content/dam/ok/en/tax/documents/forms/individuals/current/511-Pkt.pdf#page=51 - - title: 2025 Form 538-H, line 11 (gross positive income from rentals, royalties, partnerships, estates and trusts) + - title: 2025 Form 538-H, lines 10 (public assistance) and 11 (gross positive income from rentals, royalties, partnerships, estates and trusts) href: https://oklahoma.gov/content/dam/ok/en/tax/documents/forms/individuals/current/538-H.pdf#page=1 - title: Okla. Admin. Code 710:50-15-96(c) (gross household income is the gross income of every type) href: https://www.law.cornell.edu/regulations/oklahoma/OAC-710-50-15-96 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml index af54d9bca1f..596a6abf76d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/integration.yaml @@ -89,7 +89,7 @@ output: ok_ssp: [0, 0] -- name: Case 5, aged spouse who declines SSI leaves the claimant on the individual test. +- name: Case 5, aged spouse who declines SSI is an ineligible spouse whose income counts. period: 2026-01 absolute_error_margin: 0.01 input: @@ -114,12 +114,13 @@ output: # person2 does not take up, so only person1 is SSP-eligible. ok_ssp_eligible: [true, false] - # An aged spouse is not an ineligible spouse, so person1 is tested alone. + # 317:35-5-42(k)(1): a spouse who is not eligible is an ineligible spouse. # Federal couple SSI: (500 + 1_500 - 20) / 2 = 990 > 745.5, so 0. - # Own income 500 - 20 = 480 + 0 SSI; min(1_034 - 480, 40) = 40. - ok_ssp: [40, 0] + # Own income 500 - 20 = 480 < 1_034, but combined income + # 500 + 1_500 - 20 = 1_980 >= 1_532, so 0. + ok_ssp: [0, 0] -- name: Case 6, disabled spouse barred from SSP leaves the claimant on the individual test. +- name: Case 6, disabled spouse barred from SSP is an ineligible spouse whose income counts. period: 2026-01 absolute_error_margin: 0.01 input: @@ -145,10 +146,10 @@ output: # person2 is disabled but excluded, so only person1 is SSP-eligible. ok_ssp_eligible: [true, false] - # A disabled spouse is not an ineligible spouse; the 1_500 SSDI is not - # combined. Federal couple SSI: (500 + 1_500 - 20) / 2 = 990 > 745.5, so 0. - # Own income 480 + 0 SSI; min(1_034 - 480, 40) = 40. - ok_ssp: [40, 0] + # 317:35-5-42(k)(1): the barred spouse is an ineligible spouse, so the + # 1_500 SSDI is combined. Federal couple SSI: (500 + 1_500 - 20) / 2 = 990 + # > 745.5, so 0. Combined income 1_980 >= 1_532, so 0. + ok_ssp: [0, 0] - name: Case 7, spouse child allocation supports recipient. period: 2026-01 @@ -243,6 +244,7 @@ age: 65 output: ok_ssp: 480 + household_state_benefits: 480 snap_unearned_income: 12_408 hud_unearned_income: 12_408 @@ -446,3 +448,93 @@ # Combined: (3_467 - 696 - 85) / 2 = 1_343 + 147.17 = 1_490.17; # min(1_532 - 1_490.17, 40) = 40. ok_ssp: [40, 0, 0, 0] + +- name: Case 16, ineligible spouse's in-kind support and maintenance is excluded from the couple test. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi_receives_outside_shelter_support: true + person2: + age: 62 + social_security_retirement: 8_400 + ssi_receives_outside_shelter_support: true + marital_units: + couple: + members: [person1, person2] + families: + family: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OK + output: + # Deeming presumed maximum value 1_491 / 6 + 10 = 258.50 for the claimant; + # the ineligible spouse's own 994 / 3 + 20 = 351.33 is excluded by + # 317:35-5-42(l)(12). + ssi_in_kind_support_and_maintenance: [258.5, 351.33] + # Federal: 1_491 - (258.50 - 20 + 700 - 20) = 572.50 (the deeming + # computation applies the $20 exclusion twice; issue 10243). + ssi: [572.5, 0] + # Combined: 258.50 + 700 - 20 + 572.50 = 1_511, the same as when only + # person1 receives the support; min(1_532 - 1_511, 40) = 21. + ok_ssp_countable_income: [1_511, 1_511] + ok_ssp: [21, 0] + +- name: Case 17, eligible couple's shared in-kind support and maintenance counts once. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi_receives_outside_shelter_support: true + person2: + age: 68 + ssi_receives_outside_shelter_support: true + marital_units: + couple: + members: [person1, person2] + families: + family: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OK + output: + # The couple presumed maximum value 1_491 / 3 + 20 = 517 is counted once: + # SSI (745.50 - (517 - 20) / 2) = 497 each. + ssi: [497, 497] + # 517 - 20 + 497 + 497 = 1_491; 1_571 - 1_491 = 80, split [40, 40]. + ok_ssp_countable_income: [1_491, 1_491] + ok_ssp: [40, 40] + +- name: Case 18, ineligible spouse standard follows the projected 2027 SSI cost-of-living adjustment. + period: 2027-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + person2: + age: 63 + social_security_retirement: 18_480 + marital_units: + couple: + members: [person1, person2] + families: + family: + members: [person1, person2] + households: + hh: + members: [person1, person2] + state_code: OK + output: + # The Case 3 household in 2027: the projected couple FBR (about 1_532.75) + # leaves partial SSI, so combined income is about 1_532.75. The uprated + # 1_575 standard keeps the 40 maximum; a frozen 1_532 would pay 0. + ok_ssp: [40, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_gross_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_gross_income.yaml index 72d1c4817d2..ac934f30090 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_gross_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_gross_income.yaml @@ -57,3 +57,16 @@ # 18,000 is within the 20,000 limit, so the credit is 40 for one person. ok_gross_income: 18_000 ok_stc: 40 + +- name: OK gross income counts the state supplement, which can end the property tax credit + period: 2026 + input: + age: 70 + real_estate_taxes: 500 + state_code: OK + output: + # Form 538-H line 10 counts public assistance: SSI 994 x 12 = 11,928 plus + # the 40 x 12 = 480 state supplement is 12,408, over the 12,000 limit. + ok_ssp: 480 + ok_gross_income: 12_408 + ok_ptc: 0 diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py index 4c287f6467a..c3c0d1a2017 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py @@ -15,6 +15,8 @@ def ok_ssp_income(person, period, parameters, *, combined): for source in ssi.income.sources.unearned: if source not in p.excluded_unearned_sources: unearned = unearned + np.floor(max_(person(source, period), 0) + 0.5) + # 317:35-5-42(f)(11) excludes one-third of a child's child support. + # Federal SSI does not apply 20 CFR 416.1124(c)(11) yet (issue 10242). child = person("is_child", period.this_year) unearned = max_( unearned @@ -23,10 +25,11 @@ def ok_ssp_income(person, period, parameters, *, combined): * ssi.income.exclusions.child_support_share, 0, ) - unearned = ( - unearned - + person("ssi_unearned_income_deemed_from_ineligible_parent", period) - + person("ssi_in_kind_support_and_maintenance", period) + # NOTE: the deemed parental income uses the federal ineligible-child + # allocation (couple FBR minus individual FBR, 497 in 2026); Appendix C-1 + # Schedule VIII.C lists 498, so a sibling's allocation can be $1 lower. + unearned = unearned + person( + "ssi_unearned_income_deemed_from_ineligible_parent", period ) earned = max_( earned @@ -44,17 +47,31 @@ def ok_ssp_income(person, period, parameters, *, combined): * where(joint, ssi.amount.couple / 2, ssi.amount.individual) * ssi.amount.one_third_reduction_rate ) + # ssi_pmv_amount gives each member of an SSI couple the whole couple + # presumed maximum value, which federal SSI counts once per couple. + # NOTE: federal SSI also runs a couple computation for an aged, blind or + # disabled spouse who is not eligible (issue 10243), so the half equals the + # deeming value (1/6 couple FBR + $10) even when 317:35-5-42(m)(4) deems + # nothing and the individual value would apply. + ism = person("ssi_in_kind_support_and_maintenance", period) + ism = where(joint, ism / 2, ism) if combined: eligible = person("ok_ssp_eligible", period) + # 317:35-5-42(l)(12) excludes an ineligible spouse's in-kind support + # and maintenance. + unearned = unearned + ism * eligible allocation = ~eligible * person("ok_ssp_spouse_child_allocation", period) earned = max_(earned - max_(allocation - unearned, 0), 0) unearned = max_(unearned - allocation, 0) earned = person.marital_unit.sum(earned) unearned = person.marital_unit.sum(unearned) - benefit = person.marital_unit.sum(benefit) + # 317:35-5-42(l)(2) excludes an ineligible spouse's SSI. + benefit = person.marital_unit.sum(benefit * eligible) # 317:35-5-42(b)(8)(A) counts the one-third value once, for the # individual or the eligible couple, never for an ineligible spouse. shelter = person.marital_unit.sum(shelter * eligible) + else: + unearned = unearned + ism countable = ( _apply_ssi_exclusions( earned * MONTHS_IN_YEAR, unearned * MONTHS_IN_YEAR, parameters, period diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py index e8c82819588..83cccfe156d 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp.py @@ -7,27 +7,37 @@ class ok_ssp(Variable): label = "Oklahoma State Supplemental Payment" definition_period = MONTH unit = USD - reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" + reference = ( + "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5", + "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-4", + "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-2", + "https://www.oklahoma.gov/ohca/policies-and-rules/xpolicy/medical-assistance-for-adults-and-children-eligibility/eligibility-and-countable-income/countable-income-and-resources/determination-of-countable-income-for-individuals-categorically-.html", + ) defined_for = "ok_ssp_eligible" def formula(person, period, parameters): p = parameters(period).gov.states.ok.dhs.ssp ssi = parameters(period).gov.ssa.ssi.amount + # OAC 340:15-1-4(9)(B)(i): countable income must be less than 100 + # percent of the FBR plus the maximum SSP amount. individual_standard = ssi.individual + p.maximum_payment couple_standard = ssi.couple + 2 * p.maximum_payment - recipients = person.marital_unit.sum(person("ok_ssp_eligible", period)) - joint = recipients == 2 - # OAC 340:15-1-2: an ineligible spouse lives with the individual and is - # not aged, blind or disabled. With any other spouse the claimant is an - # individual. Essential spouses (in the case before 1974) are not - # modeled separately. - abd = person("is_ssi_aged_blind_disabled", period.this_year) + eligible = person("ok_ssp_eligible", period) + joint = person.marital_unit.sum(eligible) == 2 + # NOTE: OAC 340:15-1-2 defines an ineligible spouse as one who is not + # aged, blind or disabled, and is silent on an aged, blind or disabled + # spouse who is not eligible. 340:15-1-4(10) applies 317:35-5-42, whose + # (k)(1) defines an ineligible spouse as one living in the household + # who "is not eligible for Supplemental Security Income (SSI)", as SSI + # deeming does. Any spouse living together who is not SSP-eligible is + # therefore treated as an ineligible spouse. Essential spouses (in the + # case before 1974) are not modeled separately. arrangement = person("ssi_federal_living_arrangement", period) in_facility = person("ssi_lives_in_medical_treatment_facility", period) | ( arrangement == arrangement.possible_values.MEDICAL_TREATMENT_FACILITY ) apart = in_facility | person("is_incarcerated", period) - ineligible_spouse = person.marital_unit.sum(~abd & ~apart) > 0 + ineligible_spouse = person.marital_unit.sum(~eligible & ~apart) > 0 own = person("ok_ssp_own_countable_income", period) combined = person("ok_ssp_countable_income", period) standard = where( @@ -43,5 +53,9 @@ def formula(person, period, parameters): first_share = np.floor(gap / 2) first = person.marital_unit.reference_entity.members_position == 0 payment = where(joint, where(first, first_share, gap - first_share), gap) + # NOTE: 340:15-1-5(c)(6) tests a blind or disabled minor's income plus + # deemed parental income against the SSP standard for an individual, + # while 340:15-1-4(9)(B)(iii) and Appendix C-1 Schedule VIII.C say it + # must be less than the FBR. The model follows the (c)(6) procedure. own_test = joint | (own < individual_standard) return own_test * min_(payment, p.maximum_payment) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_excluded.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_excluded.py index c1789c14f50..3d378ea9de7 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_excluded.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_is_excluded.py @@ -7,4 +7,11 @@ class ok_ssp_is_excluded(Variable): label = "Oklahoma SSP exclusion applies" definition_period = MONTH reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" - documentation = "Public facility, psychiatric stay over 30 days, prohibited resource transfer, unmet benefit pursuit, or state-paid out-of-home child placement under 340:15-1-5(b)." + documentation = ( + "Public facility, psychiatric stay over 30 days, prohibited resource " + "transfer, unmet benefit pursuit, or state-paid out-of-home child " + "placement under 340:15-1-5(b). For a spouse who lives away in a " + "facility, also set ssi_lives_in_medical_treatment_facility or " + "is_incarcerated; otherwise the excluded spouse is treated as an " + "ineligible spouse living in the household." + ) diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py index c8424beda1e..b274e4ea099 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/ok_ssp_spouse_child_allocation.py @@ -8,7 +8,7 @@ class ok_ssp_spouse_child_allocation(Variable): definition_period = MONTH unit = USD reference = "https://www.law.cornell.edu/regulations/oklahoma/OAC-340-15-1-5" - documentation = "Default uses the family's non-SSI children. Override with the verified allocation for blended families or children in a TANF case." + documentation = "The total allocation for the family's children who are not aged, blind or disabled, each reduced by that child's own income. Blended families and children in a TANF case are not modeled separately." def formula(person, period, parameters): p = parameters(period).gov.states.ok.dhs.ssp From 8d2de3b22fa5c38187fb682d61d15b13d7fc32e1 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 15 Oct 2026 16:09:57 -0400 Subject: [PATCH 5/5] Exclude pre-tax health premiums from Oklahoma SSP wages Mirror main's ssi_earned_income exclusion (POMS SI 00820.102) in the per-source rounding loop. Co-Authored-By: Claude Opus 5.5 --- .../gov/states/ok/dhs/ssp/ok_ssp.yaml | 19 +++++++++++++++++++ .../gov/states/ok/dhs/ssp/_ok_ssp_income.py | 8 +++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml index fccc9b78416..8a4616a8caa 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/dhs/ssp/ok_ssp.yaml @@ -224,3 +224,22 @@ ok_ssp_eligible: false # defined_for ok_ssp_eligible, so 0. ok_ssp: 0 + +- name: Case 21, pre-tax health premiums are not counted as wages. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: OK + age: 45 + meets_ssi_disability_criteria: true + # Full-time work at the $7.25 Oklahoma minimum wage: 7.25 x 40 x 52. + employment_income: 15_080 + pre_tax_health_insurance_premiums: 1_200 + output: + # Federal: (15_080 - 1_200) / 12 = 1_156.67; (1_156.67 - 85) / 2 = 535.83; + # SSI 994 - 535.83 = 458.17. + ssi: 458.17 + # 317:35-5-42(i): wages net of premiums round to 1_157; + # (1_157 - 85) / 2 = 536 + 458.17 = 994.17; min(1_034 - 994.17, 40) = 39.83. + ok_ssp_own_countable_income: 994.17 + ok_ssp: 39.83 diff --git a/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py b/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py index c3c0d1a2017..485d136b1d7 100644 --- a/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py +++ b/policyengine_us/variables/gov/states/ok/dhs/ssp/_ok_ssp_income.py @@ -10,7 +10,13 @@ def ok_ssp_income(person, period, parameters, *, combined): # 317:35-5-42(i) rounds each monthly source before applying exclusions. earned = 0 for source in ssi.income.sources.earned: - earned = earned + np.floor(max_(person(source, period), 0) + 0.5) + amount = max_(person(source, period), 0) + if source == "employment_income": + # As in ssi_earned_income, salary-reduction health premiums are + # not wages (POMS SI 00820.102). + premiums = max_(person("pre_tax_health_insurance_premiums", period), 0) + amount = amount - min_(amount, premiums) + earned = earned + np.floor(amount + 0.5) unearned = 0 for source in ssi.income.sources.unearned: if source not in p.excluded_unearned_sources: