From 84801fae14630f8ade1d960d202e004078d86c23 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 12 Oct 2026 14:21:32 -0400 Subject: [PATCH 1/6] Add North Carolina state supplementation for 2026 --- changelog.d/nc-ssp-2026.added.md | 1 + .../household/household_state_benefits.yaml | 4 + .../hud/annual_income/sources/unearned.yaml | 3 +- .../states/nc/dhhs/sa/income_limit/basic.yaml | 12 + .../nc/dhhs/sa/income_limit/enhanced.yaml | 12 + .../states/nc/dhhs/sa/minimum_adult_age.yaml | 12 + .../nc/dhhs/sa/personal_needs_allowance.yaml | 11 + .../gov/states/nc/dhhs/sa/rate/basic.yaml | 11 + .../gov/states/nc/dhhs/sa/rate/enhanced.yaml | 11 + .../usda/snap/income/sources/unearned.yaml | 7 +- policyengine_us/programs.yaml | 12 + .../gov/states/nc/dhhs/sa/integration.yaml | 139 +++++++ .../baseline/gov/states/nc/dhhs/sa/nc_sa.yaml | 360 ++++++++++++++++++ .../variables/gov/states/nc/dhhs/sa/nc_sa.py | 37 ++ .../states/nc/dhhs/sa/nc_sa_care_setting.py | 23 ++ .../nc/dhhs/sa/nc_sa_countable_for_hud.py | 16 + .../nc/dhhs/sa/nc_sa_countable_income.py | 45 +++ .../states/nc/dhhs/sa/nc_sa_days_eligible.py | 16 + .../gov/states/nc/dhhs/sa/nc_sa_eligible.py | 54 +++ .../states/nc/dhhs/sa/nc_sa_facility_rate.py | 23 ++ .../income/spm_unit/spm_unit_benefits.py | 1 + 21 files changed, 805 insertions(+), 5 deletions(-) create mode 100644 changelog.d/nc-ssp-2026.added.md create mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml create mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml create mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml create mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml create mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml create mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml create mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py create mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py create mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py create mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py create mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py create mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py create mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py diff --git a/changelog.d/nc-ssp-2026.added.md b/changelog.d/nc-ssp-2026.added.md new file mode 100644 index 00000000000..5b3aeacd07f --- /dev/null +++ b/changelog.d/nc-ssp-2026.added.md @@ -0,0 +1 @@ +Add North Carolina State and County Special Assistance 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..45b3fb3e1b9 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 + - nc_sa - ks_sspp # Hawaii benefits - hi_oss @@ -176,6 +177,7 @@ values: # DC benefits - dc_ossp # Kansas benefits + - nc_sa - ks_sspp # Hawaii benefits - hi_oss @@ -279,6 +281,7 @@ values: - dc_ossp - dc_child_care_subsidies # Kansas benefits + - nc_sa - ks_sspp # Hawaii benefits - hi_oss @@ -382,6 +385,7 @@ values: - dc_ossp - dc_child_care_subsidies # Kansas benefits + - nc_sa - 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..719a5e9b8d1 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 + - nc_sa_countable_for_hud - general_assistance - unemployment_compensation - child_support_received @@ -34,5 +35,5 @@ metadata: period: year label: HUD unearned income sources reference: - - title: 24 CFR § 5.609(a)(1), (a)(2) + - title: 24 CFR § 5.609(a)(1), (a)(2), (b)(6), (b)(19) href: https://www.law.cornell.edu/cfr/text/24/5.609 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml new file mode 100644 index 00000000000..53b9ad1caea --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml @@ -0,0 +1,12 @@ +description: North Carolina requires monthly countable income below this threshold for the basic rate under its Special Assistance program. +values: + 2026-01-01: 1_466.51 +metadata: + unit: currency-USD + period: month + label: North Carolina SA basic exclusive income limit + reference: + - title: North Carolina Special Assistance Manual SA-3220 II.E.3 + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207 + - title: North Carolina Special Assistance In-Home Program Manual SA-5300 IV.A.3 + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=41 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml new file mode 100644 index 00000000000..234c8718c33 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml @@ -0,0 +1,12 @@ +description: North Carolina requires monthly countable income below this threshold for the enhanced rate under its Special Assistance program. +values: + 2026-01-01: 1_861.51 +metadata: + unit: currency-USD + period: month + label: North Carolina SA enhanced exclusive income limit + reference: + - title: North Carolina Special Assistance Manual SA-3220 II.E.4 + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207 + - title: North Carolina Special Assistance In-Home Program Manual SA-5300 IV.A.4 + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=41 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml new file mode 100644 index 00000000000..951f4b531bf --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml @@ -0,0 +1,12 @@ +description: North Carolina requires disabled Special Assistance applicants and in-home participants to + be at least this age. +values: + '2026-01-01': 18 +metadata: + unit: year + label: North Carolina SA minimum adult age + reference: + - title: North Carolina Special Assistance Manual SA-3100 III + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf + - title: North Carolina In-Home Manual SA-5200 + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml new file mode 100644 index 00000000000..c2db713534d --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml @@ -0,0 +1,11 @@ +description: North Carolina provides this monthly personal needs allowance under its Special Assistance + program. +values: + '2022-01-01': 70 +metadata: + unit: currency-USD + label: North Carolina SA personal needs allowance + reference: + - title: North Carolina Special Assistance Manual SA-3220 II.C + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf + period: month diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml new file mode 100644 index 00000000000..19533457ec9 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml @@ -0,0 +1,11 @@ +description: North Carolina pays up to this monthly basic rate before the personal needs allowance and + income deductions under its Special Assistance program. +values: + '2026-01-01': 1397 +metadata: + unit: currency-USD + label: North Carolina SA basic rate + reference: + - title: North Carolina Special Assistance Manual SA-3220 II and VIII + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf + period: month diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml new file mode 100644 index 00000000000..ce4f83cd4d1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml @@ -0,0 +1,11 @@ +description: North Carolina pays up to this monthly enhanced rate before the personal needs allowance + and income deductions under its Special Assistance program. +values: + '2026-01-01': 1792 +metadata: + unit: currency-USD + label: North Carolina SA enhanced rate + reference: + - title: North Carolina Special Assistance Manual SA-3220 II and VIII + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf + 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..83efb8a5cf0 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 + - nc_sa - 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..3bb206f6947 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: NC + status: partial + name: North Carolina SA + full_name: North Carolina State and County Special Assistance + variable: nc_sa + parameter_prefix: gov.states.nc.dhhs.sa + verified_years: '2026' + notes: Models basic and enhanced facility and in-home payments, lower contracted rates + and first-month proration. Certified settings include the residency waiting period, + approved care, and benefit-pursuit conditions. Separate SSI claims, agency income adjustments, + specialized exclusions and discharge settlements require explicit inputs or further + implementation. - id: social_security name: Social Security diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml new file mode 100644 index 00000000000..c70300b5e8f --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml @@ -0,0 +1,139 @@ +- name: Case 1, two in home recipients. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + nc_sa_care_setting: IN_HOME + second: + age: 65 + nc_sa_care_setting: IN_HOME + marital_units: + couple: + members: + - first + - second + tax_units: + unit: + members: + - first + - second + households: + household: + members: + - first + - second + state_code: NC + output: + nc_sa: + - 722 + - 722 +- name: Case 2, one in home recipient with spouse income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + nc_sa_care_setting: IN_HOME + second: + age: 65 + social_security_retirement: 14400 + marital_units: + couple: + members: + - first + - second + tax_units: + unit: + members: + - first + - second + households: + household: + members: + - first + - second + state_code: NC + output: + nc_sa: + - 1312 + - 0 +- name: Case 3, ineligible spouse income is not directly counted. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + first: + age: 65 + nc_sa_care_setting: IN_HOME + second: + age: 40 + social_security_retirement: 60000 + marital_units: + couple: + members: + - first + - second + tax_units: + unit: + members: + - first + - second + households: + household: + members: + - first + - second + state_code: NC + output: + nc_sa: + - 1467 + - 0 +- name: Case 4, annual in home benefit and SNAP income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME + output: + nc_sa: 5676 + household_state_benefits: 5676 + snap_unearned_income: 17604 +- name: Case 5, in home household reduction exception. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + output: + nc_sa: 804 +- name: Case 6, individual facility budget restores the federal rate. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + output: + nc_sa: 473 +- name: North Carolina in-home assistance counts for SNAP and is excluded for HUD + period: '2026' + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME + output: + nc_sa: 5676 + snap_unearned_income: 17604 + hud_unearned_income: 11928 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml new file mode 100644 index 00000000000..e389462ab53 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml @@ -0,0 +1,360 @@ +- name: Case 1, basic facility with full SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + output: + nc_sa: 473 +- name: Case 2, enhanced special care unit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: SPECIAL_CARE_UNIT + output: + nc_sa: 868 +- name: Case 3, basic in home. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME + output: + nc_sa: 473 +- name: Case 4, enhanced in home. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME_ENHANCED + output: + nc_sa: 868 +- name: Case 5, income above federal SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + social_security_retirement: 15600 + output: + nc_sa: 187 +- name: Case 6, SSI and other income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + social_security_retirement: 12000 + output: + nc_sa: 473 +- name: Case 7, earned income with SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + employment_income: 7200 + output: + nc_sa: 473 +- name: Case 8, basic half dollar rounds up. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_countable_income: 1466.5 + output: + nc_sa: 1 +- name: Case 9, basic forty nine cents rounds down. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_countable_income: 1466.51 + output: + nc_sa: 0 +- name: Case 10, enhanced half dollar rounds up. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: SPECIAL_CARE_UNIT + nc_sa_countable_income: 1861.5 + output: + nc_sa: 1 +- name: Case 11, enhanced forty nine cents rounds down. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: SPECIAL_CARE_UNIT + nc_sa_countable_income: 1861.51 + output: + nc_sa: 0 +- name: Case 12, lower contracted facility rate. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_facility_rate: 1000 + output: + nc_sa: 76 +- name: Case 13, no qualifying care setting. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: NONE + output: + nc_sa: 0 +- name: Case 14, out of state. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VA + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + output: + nc_sa: 0 +- name: Case 15, disabled child is not eligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 17 + nc_sa_care_setting: ADULT_CARE_HOME + meets_ssi_disability_criteria: true + output: + nc_sa: 0 +- name: Case 16, blind child in certified specialized center. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 17 + nc_sa_care_setting: ADULT_CARE_HOME + is_blind: true + output: + nc_sa: 473 +- name: Case 17, blind child cannot receive in home program. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 17 + nc_sa_care_setting: IN_HOME + is_blind: true + output: + nc_sa: 0 +- name: Case 18, adult disabled applicant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 18 + nc_sa_care_setting: ADULT_CARE_HOME + meets_ssi_disability_criteria: true + output: + nc_sa: 473 +- name: Case 19, not aged blind disabled. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 64 + nc_sa_care_setting: ADULT_CARE_HOME + output: + nc_sa: 0 +- name: Case 20, resource boundary. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + ssi_countable_resources: 2000 + output: + nc_sa: 473 +- name: Case 21, excess resources. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + ssi_countable_resources: 2001 + output: + nc_sa: 0 +- name: Case 22, first month twenty seven days ignores income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_days_eligible: 27 + social_security_retirement: 15600 + output: + nc_sa: 1287 +- name: Case 23, first month twenty days in home. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME + nc_sa_days_eligible: 20 + output: + nc_sa: 971 +- name: Case 24, first month fourteen days enhanced in home. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME_ENHANCED + nc_sa_days_eligible: 14 + output: + nc_sa: 879 +- name: Case 25, zero days. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_days_eligible: 0 + output: + nc_sa: 0 +- name: Case 26, SSI opt out. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + takes_up_ssi_if_eligible: false + output: + nc_sa: 0 + +# SA-3220 II.E and IV.A: strict countable-income ceilings apply before initial-month payment. +# Gross monthly Social Security less the $20 disregard gives the tested limits. +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207 +- name: Case 27, income-ineligible basic entrant receives no partial-month grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + social_security_retirement: 36000 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_days_eligible: 15 + output: + nc_sa: 0 +- name: Case 28, income-ineligible basic entrant receives no full-month grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + social_security_retirement: 36000 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_days_eligible: 31 + output: + nc_sa: 0 +- name: Case 29, basic last eligible cent retains the partial-month grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + social_security_retirement: 17838 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_days_eligible: 15 + output: + nc_sa: 746 +- name: Case 30, basic exact exclusive income limit prevents partial payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + social_security_retirement: 17838.12 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_days_eligible: 15 + output: + nc_sa: 0 +- name: Case 31, enhanced last eligible cent retains the partial-month grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + social_security_retirement: 22578 + nc_sa_care_setting: SPECIAL_CARE_UNIT + nc_sa_days_eligible: 15 + output: + nc_sa: 937 +- name: Case 32, enhanced exact exclusive income limit prevents partial payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + social_security_retirement: 22578.12 + nc_sa_care_setting: SPECIAL_CARE_UNIT + nc_sa_days_eligible: 15 + output: + nc_sa: 0 +- name: Case 33, enhanced in-home exact income limit also prevents partial payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + social_security_retirement: 22578.12 + nc_sa_care_setting: IN_HOME_ENHANCED + nc_sa_days_eligible: 15 + output: + nc_sa: 0 +- name: Case 34, last eligible cent yields one dollar for a full basic month. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + social_security_retirement: 17838 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_days_eligible: 31 + output: + nc_sa: 1 + +# The earned-income exclusion halves the remaining amount; preserve fractional cents. +# Monthly earnings 3018.01 less 20 and 65, divided by 2, give 1466.505 < 1466.51. +- name: Case 35, a half-cent countable earned-income amount below the limit remains eligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + employment_income: 36216.12 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_days_eligible: 15 + output: + nc_sa: 746 diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py new file mode 100644 index 00000000000..b503780ce02 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py @@ -0,0 +1,37 @@ +from policyengine_us.model_api import * + + +class nc_sa(Variable): + value_type = float + entity = Person + label = "North Carolina State and County Special Assistance" + definition_period = MONTH + unit = USD + defined_for = "nc_sa_eligible" + reference = ( + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.nc.dhhs.sa + setting = person("nc_sa_care_setting", period) + settings = setting.possible_values + enhanced = (setting == settings.SPECIAL_CARE_UNIT) | ( + setting == settings.IN_HOME_ENHANCED + ) + published_rate = where(enhanced, p.rate.enhanced, p.rate.basic) + in_home = (setting == settings.IN_HOME) | (setting == settings.IN_HOME_ENHANCED) + rate = where( + in_home, + published_rate, + min_(max_(person("nc_sa_facility_rate", period), 0), published_rate), + ) + income = person("nc_sa_countable_income", period) + full = np.floor(max_(rate + p.personal_needs_allowance - income, 0) + 0.5) + days = clip(person("nc_sa_days_eligible", period), 0, period.days) + # For the first partial month, income is not deducted and the full + # personal-needs allowance is added after prorating the facility rate. + daily = np.floor(rate.astype(np.float64) / period.days * 100 + 0.5) / 100 + partial = np.floor(daily * days + p.personal_needs_allowance + 0.5) + return (days > 0) * where(days == period.days, full, partial) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py new file mode 100644 index 00000000000..ee711491464 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class NCSACareSetting(Enum): + NONE = "No approved Special Assistance care setting" + ADULT_CARE_HOME = "Approved basic residential care" + SPECIAL_CARE_UNIT = "Approved enhanced special care unit" + IN_HOME = "Approved basic in-home care" + IN_HOME_ENHANCED = "Approved enhanced in-home care" + + +class nc_sa_care_setting(Variable): + value_type = Enum + entity = Person + label = "North Carolina Special Assistance certified care setting" + definition_period = MONTH + possible_values = NCSACareSetting + default_value = NCSACareSetting.NONE + documentation = "A positive category indicates an approved placement or in-home service plan with a valid FL-2, satisfaction of the state residency waiting period or exception, pursuit of other benefits, and no conflicting PACE or waiver enrollment. Enhanced settings require the qualifying dementia diagnosis. A blind child requires a certified specialized community residential center." + reference = ( + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + ) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py new file mode 100644 index 00000000000..7327cc1c9e8 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class nc_sa_countable_for_hud(Variable): + value_type = float + entity = Person + label = "North Carolina State Assistance countable for HUD" + definition_period = MONTH + unit = USD + reference = "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf" + + def formula(person, period, parameters): + category = person("nc_sa_care_setting", period) + c = category.possible_values + excluded = (category == c.IN_HOME) | (category == c.IN_HOME_ENHANCED) + return person("nc_sa", period) * ~excluded diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py new file mode 100644 index 00000000000..45523f343a1 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py @@ -0,0 +1,45 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.ssa.ssi.eligibility.income._apply_ssi_exclusions import ( + _apply_ssi_exclusions, +) + + +class nc_sa_countable_income(Variable): + value_type = float + entity = Person + label = "North Carolina SA countable income" + definition_period = MONTH + unit = USD + reference = "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf" + + def formula(person, period, parameters): + # SA is always a one-person budget: no spousal or parental deeming. + earned = max_( + person("ssi_earned_income", period) + - person("ssi_blind_or_disabled_working_student_exclusion", period), + 0, + ) + unearned = max_(person("ssi_unearned_income", period), 0) + other_income = ( + _apply_ssi_exclusions( + earned * MONTHS_IN_YEAR, unearned * MONTHS_IN_YEAR, parameters, period + ) + / MONTHS_IN_YEAR + ) + federal = person("ssi", period) + setting = person("nc_sa_care_setting", period) + settings = setting.possible_values + facility = (setting == settings.ADULT_CARE_HOME) | ( + setting == settings.SPECIAL_CARE_UNIT + ) + # Ongoing facility budgets assume the appropriate full federal rate + # while SSA restores a prior institutional or household reduction. + # SSA couple computations remain an express exception to this rule. + individual_claim = ~person("ssi_claim_is_joint", period.this_year) + individual_fbr = parameters(period).gov.ssa.ssi.amount.individual + federal = where( + facility & individual_claim, + max_(federal, max_(individual_fbr - other_income, 0)), + federal, + ) + return other_income + federal diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py new file mode 100644 index 00000000000..cdb1f3d16ff --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class nc_sa_days_eligible(Variable): + value_type = int + entity = Person + label = "North Carolina SA eligible days in initial month" + definition_period = MONTH + documentation = "For an initial partial month, days from the date all eligibility conditions are satisfied through month end, including the first eligible day. Ongoing months default to the full month. This does not implement a discharge-month settlement." + reference = ( + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + ) + + def formula(person, period, parameters): + return period.days diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py new file mode 100644 index 00000000000..c21332511d1 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py @@ -0,0 +1,54 @@ +from policyengine_us.model_api import * + + +class nc_sa_eligible(Variable): + value_type = bool + entity = Person + label = "North Carolina Special Assistance eligibility" + definition_period = MONTH + defined_for = StateCode.NC + reference = ( + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.nc.dhhs.sa + setting = person("nc_sa_care_setting", period) + settings = setting.possible_values + age = person("age", period.this_year) + in_home = (setting == settings.IN_HOME) | (setting == settings.IN_HOME_ENHANCED) + adult = age >= p.minimum_adult_age + age_eligible = adult | (person("is_blind", period.this_year) & ~in_home) + receives_ssi = person("ssi", period) > 0 + # SSI receipt establishes the resource test. Non-SSI cases use an + # individual state budget, in addition to SSI non-income eligibility. + resource_limit = parameters( + period + ).gov.ssa.ssi.eligibility.resources.limit.individual + resources = person("ssi_countable_resources", period.this_year) + resources_eligible = receives_ssi | (resources <= resource_limit) + ssi_status = receives_ssi | person("is_ssi_eligible", period) + enhanced = (setting == settings.SPECIAL_CARE_UNIT) | ( + setting == settings.IN_HOME_ENHANCED + ) + income_limit = where(enhanced, p.income_limit.enhanced, p.income_limit.basic) + # The monthly income test still applies when the first payment is + # prorated without subtracting income (SA-3220 II.E and III). + countable_income = person("nc_sa_countable_income", period) + # Annual-to-month arithmetic can put an exact cutoff one storage + # step below it. Preserve sub-cent income; do not round the budget. + comparison_limit = np.nextafter( + income_limit.astype(countable_income.dtype), + -np.inf, + dtype=countable_income.dtype, + ) + income_eligible = countable_income < comparison_limit + return ( + (setting != settings.NONE) + & age_eligible + & resources_eligible + & ssi_status + & income_eligible + & person("takes_up_ssi_if_eligible", period.this_year) + ) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py new file mode 100644 index 00000000000..c63e51bd116 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class nc_sa_facility_rate(Variable): + value_type = float + entity = Person + label = "North Carolina SA contracted monthly care rate" + definition_period = MONTH + unit = USD + documentation = "Defaults to the published rate. Supply the actual rate if a facility contracts for less. In-home payments use the published basic or enhanced rate." + reference = ( + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.nc.dhhs.sa + setting = person("nc_sa_care_setting", period) + settings = setting.possible_values + enhanced = (setting == settings.SPECIAL_CARE_UNIT) | ( + setting == settings.IN_HOME_ENHANCED + ) + return where(enhanced, p.rate.enhanced, p.rate.basic) 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..356cd7faee6 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", + "nc_sa", "ks_sspp", # Kansas benefits "hi_oss", "la_oss", # Louisiana benefits From 8b34892966886b0419fb7ff2e3155265c75b061d Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 13 Oct 2026 00:58:59 -0400 Subject: [PATCH 2/6] Normalize North Carolina SSP parameter and test formatting --- .../household/household_state_benefits.yaml | 1 + .../hud/annual_income/sources/unearned.yaml | 1 + .../states/nc/dhhs/sa/income_limit/basic.yaml | 1 + .../nc/dhhs/sa/income_limit/enhanced.yaml | 1 + .../states/nc/dhhs/sa/minimum_adult_age.yaml | 14 +-- .../nc/dhhs/sa/personal_needs_allowance.yaml | 12 +-- .../gov/states/nc/dhhs/sa/rate/basic.yaml | 12 +-- .../gov/states/nc/dhhs/sa/rate/enhanced.yaml | 12 +-- .../gov/states/nc/dhhs/sa/integration.yaml | 86 ++++++++----------- .../baseline/gov/states/nc/dhhs/sa/nc_sa.yaml | 74 +++++++++++----- 10 files changed, 116 insertions(+), 98 deletions(-) diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index 45b3fb3e1b9..df3e8836597 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 719a5e9b8d1..0fb4d5f20ce 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/states/nc/dhhs/sa/income_limit/basic.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml index 53b9ad1caea..1ed1da8973b 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml @@ -1,6 +1,7 @@ description: North Carolina requires monthly countable income below this threshold for the basic rate under its Special Assistance program. values: 2026-01-01: 1_466.51 + metadata: unit: currency-USD period: month diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml index 234c8718c33..8c98c4ac873 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml @@ -1,6 +1,7 @@ description: North Carolina requires monthly countable income below this threshold for the enhanced rate under its Special Assistance program. values: 2026-01-01: 1_861.51 + metadata: unit: currency-USD period: month diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml index 951f4b531bf..a2f1174eefe 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml @@ -1,12 +1,12 @@ -description: North Carolina requires disabled Special Assistance applicants and in-home participants to - be at least this age. +description: North Carolina requires disabled Special Assistance applicants and in-home participants to be at least this age. values: - '2026-01-01': 18 + 2026-01-01: 18 + metadata: unit: year label: North Carolina SA minimum adult age reference: - - title: North Carolina Special Assistance Manual SA-3100 III - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf - - title: North Carolina In-Home Manual SA-5200 - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf + - title: North Carolina Special Assistance Manual SA-3100 III + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf + - title: North Carolina In-Home Manual SA-5200 + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml index c2db713534d..6e5d5da20aa 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml @@ -1,11 +1,11 @@ -description: North Carolina provides this monthly personal needs allowance under its Special Assistance - program. +description: North Carolina provides this monthly personal needs allowance under its Special Assistance program. values: - '2022-01-01': 70 + 2022-01-01: 70 + metadata: unit: currency-USD + period: month label: North Carolina SA personal needs allowance reference: - - title: North Carolina Special Assistance Manual SA-3220 II.C - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf - period: month + - title: North Carolina Special Assistance Manual SA-3220 II.C + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml index 19533457ec9..7767ddbb274 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml @@ -1,11 +1,11 @@ -description: North Carolina pays up to this monthly basic rate before the personal needs allowance and - income deductions under its Special Assistance program. +description: North Carolina pays up to this monthly basic rate before the personal needs allowance and income deductions under its Special Assistance program. values: - '2026-01-01': 1397 + 2026-01-01: 1_397 + metadata: unit: currency-USD + period: month label: North Carolina SA basic rate reference: - - title: North Carolina Special Assistance Manual SA-3220 II and VIII - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf - period: month + - title: North Carolina Special Assistance Manual SA-3220 II and VIII + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml index ce4f83cd4d1..83276d333cc 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml @@ -1,11 +1,11 @@ -description: North Carolina pays up to this monthly enhanced rate before the personal needs allowance - and income deductions under its Special Assistance program. +description: North Carolina pays up to this monthly enhanced rate before the personal needs allowance and income deductions under its Special Assistance program. values: - '2026-01-01': 1792 + 2026-01-01: 1_792 + metadata: unit: currency-USD + period: month label: North Carolina SA enhanced rate reference: - - title: North Carolina Special Assistance Manual SA-3220 II and VIII - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf - period: month + - title: North Carolina Special Assistance Manual SA-3220 II and VIII + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml index c70300b5e8f..7a9b8bc6c00 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml @@ -3,94 +3,73 @@ absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 nc_sa_care_setting: IN_HOME - second: + person2: age: 65 nc_sa_care_setting: IN_HOME marital_units: couple: - members: - - first - - second + members: [person1, person2] tax_units: unit: - members: - - first - - second + members: [person1, person2] households: household: - members: - - first - - second + members: [person1, person2] state_code: NC output: - nc_sa: - - 722 - - 722 + nc_sa: [722, 722] + - name: Case 2, one in home recipient with spouse income. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 nc_sa_care_setting: IN_HOME - second: + person2: age: 65 - social_security_retirement: 14400 + social_security_retirement: 14_400 marital_units: couple: - members: - - first - - second + members: [person1, person2] tax_units: unit: - members: - - first - - second + members: [person1, person2] households: household: - members: - - first - - second + members: [person1, person2] state_code: NC output: - nc_sa: - - 1312 - - 0 + nc_sa: [1_312, 0] + - name: Case 3, ineligible spouse income is not directly counted. period: 2026-01 absolute_error_margin: 0.01 input: people: - first: + person1: age: 65 nc_sa_care_setting: IN_HOME - second: + person2: age: 40 - social_security_retirement: 60000 + social_security_retirement: 60_000 marital_units: couple: - members: - - first - - second + members: [person1, person2] tax_units: unit: - members: - - first - - second + members: [person1, person2] households: household: - members: - - first - - second + members: [person1, person2] state_code: NC output: - nc_sa: - - 1467 - - 0 + nc_sa: [1_467, 0] + - name: Case 4, annual in home benefit and SNAP income. period: 2026 absolute_error_margin: 0.01 @@ -99,9 +78,10 @@ age: 65 nc_sa_care_setting: IN_HOME output: - nc_sa: 5676 - household_state_benefits: 5676 - snap_unearned_income: 17604 + nc_sa: 5_676 + household_state_benefits: 5_676 + snap_unearned_income: 17_604 + - name: Case 5, in home household reduction exception. period: 2026-01 absolute_error_margin: 0.01 @@ -114,6 +94,7 @@ ssi_others_pay_all_meals: true output: nc_sa: 804 + - name: Case 6, individual facility budget restores the federal rate. period: 2026-01 absolute_error_margin: 0.01 @@ -126,14 +107,15 @@ ssi_others_pay_all_meals: true output: nc_sa: 473 -- name: North Carolina in-home assistance counts for SNAP and is excluded for HUD - period: '2026' + +- name: Case 7, North Carolina in-home assistance counts for SNAP and is excluded for HUD. + period: 2026 absolute_error_margin: 0.01 input: state_code: NC age: 65 nc_sa_care_setting: IN_HOME output: - nc_sa: 5676 - snap_unearned_income: 17604 - hud_unearned_income: 11928 + nc_sa: 5_676 + snap_unearned_income: 17_604 + hud_unearned_income: 11_928 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml index e389462ab53..b2e74dfbbf3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml @@ -7,6 +7,7 @@ nc_sa_care_setting: ADULT_CARE_HOME output: nc_sa: 473 + - name: Case 2, enhanced special care unit. period: 2026-01 absolute_error_margin: 0.01 @@ -16,6 +17,7 @@ nc_sa_care_setting: SPECIAL_CARE_UNIT output: nc_sa: 868 + - name: Case 3, basic in home. period: 2026-01 absolute_error_margin: 0.01 @@ -25,6 +27,7 @@ nc_sa_care_setting: IN_HOME output: nc_sa: 473 + - name: Case 4, enhanced in home. period: 2026-01 absolute_error_margin: 0.01 @@ -34,6 +37,7 @@ nc_sa_care_setting: IN_HOME_ENHANCED output: nc_sa: 868 + - name: Case 5, income above federal SSI. period: 2026-01 absolute_error_margin: 0.01 @@ -41,9 +45,10 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - social_security_retirement: 15600 + social_security_retirement: 15_600 output: nc_sa: 187 + - name: Case 6, SSI and other income. period: 2026-01 absolute_error_margin: 0.01 @@ -51,9 +56,10 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - social_security_retirement: 12000 + social_security_retirement: 12_000 output: nc_sa: 473 + - name: Case 7, earned income with SSI. period: 2026-01 absolute_error_margin: 0.01 @@ -61,9 +67,10 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - employment_income: 7200 + employment_income: 7_200 output: nc_sa: 473 + - name: Case 8, basic half dollar rounds up. period: 2026-01 absolute_error_margin: 0.01 @@ -71,9 +78,10 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_countable_income: 1466.5 + nc_sa_countable_income: 1_466.5 output: nc_sa: 1 + - name: Case 9, basic forty nine cents rounds down. period: 2026-01 absolute_error_margin: 0.01 @@ -81,9 +89,10 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_countable_income: 1466.51 + nc_sa_countable_income: 1_466.51 output: nc_sa: 0 + - name: Case 10, enhanced half dollar rounds up. period: 2026-01 absolute_error_margin: 0.01 @@ -91,9 +100,10 @@ state_code: NC age: 65 nc_sa_care_setting: SPECIAL_CARE_UNIT - nc_sa_countable_income: 1861.5 + nc_sa_countable_income: 1_861.5 output: nc_sa: 1 + - name: Case 11, enhanced forty nine cents rounds down. period: 2026-01 absolute_error_margin: 0.01 @@ -101,9 +111,10 @@ state_code: NC age: 65 nc_sa_care_setting: SPECIAL_CARE_UNIT - nc_sa_countable_income: 1861.51 + nc_sa_countable_income: 1_861.51 output: nc_sa: 0 + - name: Case 12, lower contracted facility rate. period: 2026-01 absolute_error_margin: 0.01 @@ -111,9 +122,10 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_facility_rate: 1000 + nc_sa_facility_rate: 1_000 output: nc_sa: 76 + - name: Case 13, no qualifying care setting. period: 2026-01 absolute_error_margin: 0.01 @@ -123,6 +135,7 @@ nc_sa_care_setting: NONE output: nc_sa: 0 + - name: Case 14, out of state. period: 2026-01 absolute_error_margin: 0.01 @@ -132,6 +145,7 @@ nc_sa_care_setting: ADULT_CARE_HOME output: nc_sa: 0 + - name: Case 15, disabled child is not eligible. period: 2026-01 absolute_error_margin: 0.01 @@ -142,6 +156,7 @@ meets_ssi_disability_criteria: true output: nc_sa: 0 + - name: Case 16, blind child in certified specialized center. period: 2026-01 absolute_error_margin: 0.01 @@ -152,6 +167,7 @@ is_blind: true output: nc_sa: 473 + - name: Case 17, blind child cannot receive in home program. period: 2026-01 absolute_error_margin: 0.01 @@ -162,6 +178,7 @@ is_blind: true output: nc_sa: 0 + - name: Case 18, adult disabled applicant. period: 2026-01 absolute_error_margin: 0.01 @@ -172,6 +189,7 @@ meets_ssi_disability_criteria: true output: nc_sa: 473 + - name: Case 19, not aged blind disabled. period: 2026-01 absolute_error_margin: 0.01 @@ -181,6 +199,7 @@ nc_sa_care_setting: ADULT_CARE_HOME output: nc_sa: 0 + - name: Case 20, resource boundary. period: 2026-01 absolute_error_margin: 0.01 @@ -188,9 +207,10 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - ssi_countable_resources: 2000 + ssi_countable_resources: 2_000 output: nc_sa: 473 + - name: Case 21, excess resources. period: 2026-01 absolute_error_margin: 0.01 @@ -198,9 +218,10 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - ssi_countable_resources: 2001 + ssi_countable_resources: 2_001 output: nc_sa: 0 + - name: Case 22, first month twenty seven days ignores income. period: 2026-01 absolute_error_margin: 0.01 @@ -209,9 +230,10 @@ age: 65 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 27 - social_security_retirement: 15600 + social_security_retirement: 15_600 output: - nc_sa: 1287 + nc_sa: 1_287 + - name: Case 23, first month twenty days in home. period: 2026-01 absolute_error_margin: 0.01 @@ -222,6 +244,7 @@ nc_sa_days_eligible: 20 output: nc_sa: 971 + - name: Case 24, first month fourteen days enhanced in home. period: 2026-01 absolute_error_margin: 0.01 @@ -232,6 +255,7 @@ nc_sa_days_eligible: 14 output: nc_sa: 879 + - name: Case 25, zero days. period: 2026-01 absolute_error_margin: 0.01 @@ -242,6 +266,7 @@ nc_sa_days_eligible: 0 output: nc_sa: 0 + - name: Case 26, SSI opt out. period: 2026-01 absolute_error_margin: 0.01 @@ -262,84 +287,91 @@ input: state_code: NC age: 65 - social_security_retirement: 36000 + social_security_retirement: 36_000 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 15 output: nc_sa: 0 + - name: Case 28, income-ineligible basic entrant receives no full-month grant. period: 2026-01 absolute_error_margin: 0.01 input: state_code: NC age: 65 - social_security_retirement: 36000 + social_security_retirement: 36_000 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 31 output: nc_sa: 0 + - name: Case 29, basic last eligible cent retains the partial-month grant. period: 2026-01 absolute_error_margin: 0.01 input: state_code: NC age: 65 - social_security_retirement: 17838 + social_security_retirement: 17_838 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 15 output: nc_sa: 746 + - name: Case 30, basic exact exclusive income limit prevents partial payment. period: 2026-01 absolute_error_margin: 0.01 input: state_code: NC age: 65 - social_security_retirement: 17838.12 + social_security_retirement: 17_838.12 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 15 output: nc_sa: 0 + - name: Case 31, enhanced last eligible cent retains the partial-month grant. period: 2026-01 absolute_error_margin: 0.01 input: state_code: NC age: 65 - social_security_retirement: 22578 + social_security_retirement: 22_578 nc_sa_care_setting: SPECIAL_CARE_UNIT nc_sa_days_eligible: 15 output: nc_sa: 937 + - name: Case 32, enhanced exact exclusive income limit prevents partial payment. period: 2026-01 absolute_error_margin: 0.01 input: state_code: NC age: 65 - social_security_retirement: 22578.12 + social_security_retirement: 22_578.12 nc_sa_care_setting: SPECIAL_CARE_UNIT nc_sa_days_eligible: 15 output: nc_sa: 0 + - name: Case 33, enhanced in-home exact income limit also prevents partial payment. period: 2026-01 absolute_error_margin: 0.01 input: state_code: NC age: 65 - social_security_retirement: 22578.12 + social_security_retirement: 22_578.12 nc_sa_care_setting: IN_HOME_ENHANCED nc_sa_days_eligible: 15 output: nc_sa: 0 + - name: Case 34, last eligible cent yields one dollar for a full basic month. period: 2026-01 absolute_error_margin: 0.01 input: state_code: NC age: 65 - social_security_retirement: 17838 + social_security_retirement: 17_838 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 31 output: @@ -353,7 +385,7 @@ input: state_code: NC age: 65 - employment_income: 36216.12 + employment_income: 36_216.12 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 15 output: From 223fe3fd7714f019b288854e422b9d4095b110c7 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 13 Oct 2026 02:46:27 -0400 Subject: [PATCH 3/6] Fix issues from review: North Carolina Special Assistance resources, cutoff and tests - Apply the applicant-only $2,000 resource test to non-SSI applicants (budget unit of one) - Gate SSI receipt on (ssi > 0) | receives_ssi - Compare the income cutoff at cent precision instead of a float32 step - Registry placement and comments, SNAP title, revert shared HUD metadata, shorter programs.yaml note - Special Assistance HUD helper label and SAIH/24 CFR 5.609(b)(12)(ii) references; page anchors - Realistic households, trimmed cutoff cases, per-variable tests and intermediate outputs Co-Authored-By: Claude Opus 5.5 --- .../household/household_state_benefits.yaml | 9 +- .../hud/annual_income/sources/unearned.yaml | 2 +- .../states/nc/dhhs/sa/minimum_adult_age.yaml | 9 +- .../nc/dhhs/sa/personal_needs_allowance.yaml | 2 +- .../gov/states/nc/dhhs/sa/rate/basic.yaml | 6 +- .../gov/states/nc/dhhs/sa/rate/enhanced.yaml | 6 +- .../usda/snap/income/sources/unearned.yaml | 4 +- policyengine_us/programs.yaml | 7 +- .../gov/states/nc/dhhs/sa/integration.yaml | 70 ++++++++- .../baseline/gov/states/nc/dhhs/sa/nc_sa.yaml | 98 ++++++++----- .../nc/dhhs/sa/nc_sa_countable_for_hud.yaml | 80 ++++++++++ .../nc/dhhs/sa/nc_sa_countable_income.yaml | 110 ++++++++++++++ .../gov/states/nc/dhhs/sa/nc_sa_eligible.yaml | 138 ++++++++++++++++++ .../variables/gov/states/nc/dhhs/sa/nc_sa.py | 6 +- .../states/nc/dhhs/sa/nc_sa_care_setting.py | 8 +- .../nc/dhhs/sa/nc_sa_countable_for_hud.py | 9 +- .../nc/dhhs/sa/nc_sa_countable_income.py | 4 +- .../states/nc/dhhs/sa/nc_sa_days_eligible.py | 7 +- .../gov/states/nc/dhhs/sa/nc_sa_eligible.py | 46 +++--- .../states/nc/dhhs/sa/nc_sa_facility_rate.py | 6 +- .../income/spm_unit/spm_unit_benefits.py | 2 +- 21 files changed, 541 insertions(+), 88 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_eligible.yaml diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index df3e8836597..e279ce820c0 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -67,6 +67,8 @@ values: - sc_child_care_subsidies # New Mexico benefits - nm_ssi_state_supplement + # North Carolina benefits + - nc_sa # South Carolina benefits - sc_ssi_state_supplement # Texas benefits @@ -78,7 +80,6 @@ values: # DC benefits - dc_ossp # Kansas benefits - - nc_sa - ks_sspp # Hawaii benefits - hi_oss @@ -170,6 +171,7 @@ values: - wv_child_care_subsidies # North Carolina benefits - nc_child_care_subsidies + - nc_sa # South Carolina benefits - sc_child_care_subsidies # Massachusetts benefits @@ -177,7 +179,6 @@ values: # DC benefits - dc_ossp # Kansas benefits - - nc_sa - ks_sspp # Hawaii benefits - hi_oss @@ -273,6 +274,7 @@ values: - wv_child_care_subsidies # North Carolina benefits - nc_child_care_subsidies + - nc_sa # South Carolina benefits - sc_child_care_subsidies # Massachusetts benefits @@ -281,7 +283,6 @@ values: - dc_ossp - dc_child_care_subsidies # Kansas benefits - - nc_sa - ks_sspp # Hawaii benefits - hi_oss @@ -377,6 +378,7 @@ values: - wv_child_care_subsidies # North Carolina benefits - nc_child_care_subsidies + - nc_sa # South Carolina benefits - sc_child_care_subsidies # Massachusetts benefits @@ -385,7 +387,6 @@ values: - dc_ossp - dc_child_care_subsidies # Kansas benefits - - nc_sa - 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 0fb4d5f20ce..51ff21a5717 100644 --- a/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/hud/annual_income/sources/unearned.yaml @@ -36,5 +36,5 @@ metadata: period: year label: HUD unearned income sources reference: - - title: 24 CFR § 5.609(a)(1), (a)(2), (b)(6), (b)(19) + - title: 24 CFR § 5.609(a)(1), (a)(2) href: https://www.law.cornell.edu/cfr/text/24/5.609 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml index a2f1174eefe..c9df4896683 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml @@ -4,9 +4,10 @@ values: metadata: unit: year + period: year label: North Carolina SA minimum adult age reference: - - title: North Carolina Special Assistance Manual SA-3100 III - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf - - title: North Carolina In-Home Manual SA-5200 - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf + - title: "North Carolina Special Assistance Manual SA-3100 II.A.1 and III.C #page 6,10" + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6 + - title: North Carolina Special Assistance In-Home Program Manual SA-5200 + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=14 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml index 6e5d5da20aa..514e2619b8d 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml @@ -8,4 +8,4 @@ metadata: label: North Carolina SA personal needs allowance reference: - title: North Carolina Special Assistance Manual SA-3220 II.C - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=205 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml index 7767ddbb274..715ffa1e838 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml @@ -7,5 +7,7 @@ metadata: period: month label: North Carolina SA basic rate reference: - - title: North Carolina Special Assistance Manual SA-3220 II and VIII - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf + - title: "North Carolina Special Assistance Manual SA-3220 II.A and VIII #page 204,219" + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204 + - title: North Carolina Change Notice EFS-SA-CN-01-2026 I + href: https://policies.ncdhhs.gov/wp-content/uploads/EFS-SA-CN-01-2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml index 83276d333cc..62bbd3bbf2b 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml +++ b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml @@ -7,5 +7,7 @@ metadata: period: month label: North Carolina SA enhanced rate reference: - - title: North Carolina Special Assistance Manual SA-3220 II and VIII - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf + - title: "North Carolina Special Assistance Manual SA-3220 II.B and VIII #page 205,219" + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=205 + - title: North Carolina Change Notice EFS-SA-CN-01-2026 I + href: https://policies.ncdhhs.gov/wp-content/uploads/EFS-SA-CN-01-2026.pdf#page=1 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 83efb8a5cf0..9ff09a030ed 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 - - nc_sa - ks_sspp - ky_ssp - la_oss @@ -46,6 +45,7 @@ values: - mi_ssp_person - mn_msa_person - mo_ssp + - nc_sa - ne_aabd - nm_ssi_state_supplement - sc_ssi_state_supplement @@ -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 3bb206f6947..d042f462db9 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1097,11 +1097,8 @@ programs: variable: nc_sa parameter_prefix: gov.states.nc.dhhs.sa verified_years: '2026' - notes: Models basic and enhanced facility and in-home payments, lower contracted rates - and first-month proration. Certified settings include the residency waiting period, - approved care, and benefit-pursuit conditions. Separate SSI claims, agency income adjustments, - specialized exclusions and discharge settlements require explicit inputs or further - implementation. + notes: Models 2026 basic and enhanced facility and in-home payments, including lower contracted + rates and first-month proration. Open/shut and discharge payments are not modeled. - id: social_security name: Social Security diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml index 7a9b8bc6c00..949549ece93 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml @@ -20,6 +20,15 @@ members: [person1, person2] state_code: NC output: + # Joint claim with no income: couple FBR 1,491 / 2 = 745.50 each. + ssi: [745.5, 745.5] + # Own income 0 + SSI 745.50; in-home budgets keep the couple amount. + nc_sa_countable_income: [745.5, 745.5] + # Both receive SSI; TCMI 745.50 < 1,466.51. + nc_sa_eligible: [true, true] + # In-home basic payments use the published basic rate. + nc_sa_facility_rate: [1_397, 1_397] + # 1,397 + 70 - 745.50 = 721.50 -> 722. nc_sa: [722, 722] - name: Case 2, one in home recipient with spouse income. @@ -44,8 +53,20 @@ members: [person1, person2] state_code: NC output: + # Joint claim: (1,200 - 20) / 2 = 590 countable each; 745.50 - 590 = 155.50. + ssi: [155.5, 155.5] + # person1: own income 0 + SSI 155.50. + # person2: 1,200 - 20 = 1,180 + SSI 155.50 = 1,335.50. + nc_sa_countable_income: [155.5, 1_335.5] + # person2 has no approved care setting. + nc_sa_eligible: [true, false] + nc_sa_facility_rate: [1_397, 1_397] + # 1,397 + 70 - 155.50 = 1,311.50 -> 1,312. nc_sa: [1_312, 0] +# person2 earns North Carolina's May 2023 median wage, $21.85 an hour +# (BLS OEWS, all occupations), full time: 21.85 * 2,080 = $45,448 a year. +# https://www.bls.gov/oes/2023/may/oes_nc.htm - name: Case 3, ineligible spouse income is not directly counted. period: 2026-01 absolute_error_margin: 0.01 @@ -56,7 +77,7 @@ nc_sa_care_setting: IN_HOME person2: age: 40 - social_security_retirement: 60_000 + employment_income: 45_448 marital_units: couple: members: [person1, person2] @@ -68,6 +89,16 @@ members: [person1, person2] state_code: NC output: + # SSI deems the ineligible spouse's 45,448 / 12 = 3,787.33 a month: + # (3,787.33 - 20 - 65) / 2 = 1,851.17 > 1,491 couple FBR, so SSI is 0. + ssi: [0, 0] + # SA counts only the applicant's own income (budget unit of one). + # person2: (3,787.33 - 85) / 2 = 1,851.17. + nc_sa_countable_income: [0, 1_851.17] + # person1: aged citizen with own resources 0 <= 2,000 and TCMI 0. + nc_sa_eligible: [true, false] + nc_sa_facility_rate: [1_397, 1_397] + # 1,397 + 70 - 0 = 1,467. nc_sa: [1_467, 0] - name: Case 4, annual in home benefit and SNAP income. @@ -78,8 +109,18 @@ age: 65 nc_sa_care_setting: IN_HOME output: + # 994 * 12 = 11,928. + ssi: 11_928 + # Own income 0 + SSI 994 = 994 a month; 994 * 12 = 11,928. + nc_sa_countable_income: 11_928 + nc_sa_eligible: + 2026-01: true + # 1,397 * 12 = 16,764. + nc_sa_facility_rate: 16_764 + # (1,397 + 70 - 994) * 12 = 473 * 12 = 5,676. nc_sa: 5_676 household_state_benefits: 5_676 + # SSI 11,928 + SA 5,676 = 17,604. snap_unearned_income: 17_604 - name: Case 5, in home household reduction exception. @@ -93,6 +134,14 @@ ssi_receives_shelter_from_others_in_household: true ssi_others_pay_all_meals: true output: + # One-third reduction: 994 * (1 - 1/3) = 662.67. + ssi_amount_if_eligible: 662.67 + ssi: 662.67 + # In-home budgets keep the reduced SSI (SA-3210 V.D.1.e(3)): 0 + 662.67. + nc_sa_countable_income: 662.67 + nc_sa_eligible: true + nc_sa_facility_rate: 1_397 + # 1,397 + 70 - 662.67 = 804.33 -> 804. nc_sa: 804 - name: Case 6, individual facility budget restores the federal rate. @@ -106,6 +155,14 @@ ssi_receives_shelter_from_others_in_household: true ssi_others_pay_all_meals: true output: + # One-third reduction: 994 * (1 - 1/3) = 662.67. + ssi_amount_if_eligible: 662.67 + ssi: 662.67 + # Individual facility budget uses max(662.67, 994 - 0) = 994. + nc_sa_countable_income: 994 + nc_sa_eligible: true + nc_sa_facility_rate: 1_397 + # 1,397 + 70 - 994 = 473. nc_sa: 473 - name: Case 7, North Carolina in-home assistance counts for SNAP and is excluded for HUD. @@ -116,6 +173,17 @@ age: 65 nc_sa_care_setting: IN_HOME output: + # 994 * 12 = 11,928. + ssi: 11_928 + nc_sa_countable_income: 11_928 + nc_sa_eligible: + 2026-01: true + nc_sa_facility_rate: 16_764 + # 473 * 12 = 5,676. nc_sa: 5_676 + # SAIH payments are excluded for HUD (SA-5400 III). + nc_sa_countable_for_hud: 0 + # SSI 11,928 + SA 5,676 = 17,604. snap_unearned_income: 17_604 + # SSI 11,928 + SA 0 = 11,928. hud_unearned_income: 11_928 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml index b2e74dfbbf3..c910a664b50 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml @@ -279,7 +279,7 @@ nc_sa: 0 # SA-3220 II.E and IV.A: strict countable-income ceilings apply before initial-month payment. -# Gross monthly Social Security less the $20 disregard gives the tested limits. +# Cases 29-32 enter TCMI directly at the last eligible cent and at the cutoff. # https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207 - name: Case 27, income-ineligible basic entrant receives no partial-month grant. period: 2026-01 @@ -311,10 +311,13 @@ input: state_code: NC age: 65 - social_security_retirement: 17_838 + nc_sa_countable_income: 1_466.5 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 15 output: + # TCMI 1,466.50 is below the 1,466.51 basic cutoff. + nc_sa_eligible: true + # Per diem 1,397 / 31 = 45.0645 -> 45.06; 45.06 * 15 + 70 = 745.90 -> 746. nc_sa: 746 - name: Case 30, basic exact exclusive income limit prevents partial payment. @@ -323,10 +326,12 @@ input: state_code: NC age: 65 - social_security_retirement: 17_838.12 + nc_sa_countable_income: 1_466.51 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_days_eligible: 15 output: + # TCMI 1,466.51 or more is ineligible for the basic rate. + nc_sa_eligible: false nc_sa: 0 - name: Case 31, enhanced last eligible cent retains the partial-month grant. @@ -335,10 +340,13 @@ input: state_code: NC age: 65 - social_security_retirement: 22_578 + nc_sa_countable_income: 1_861.5 nc_sa_care_setting: SPECIAL_CARE_UNIT nc_sa_days_eligible: 15 output: + # TCMI 1,861.50 is below the 1,861.51 enhanced cutoff. + nc_sa_eligible: true + # Per diem 1,792 / 31 = 57.8065 -> 57.81; 57.81 * 15 + 70 = 937.15 -> 937. nc_sa: 937 - name: Case 32, enhanced exact exclusive income limit prevents partial payment. @@ -347,46 +355,68 @@ input: state_code: NC age: 65 - social_security_retirement: 22_578.12 + nc_sa_countable_income: 1_861.51 nc_sa_care_setting: SPECIAL_CARE_UNIT nc_sa_days_eligible: 15 output: + # TCMI 1,861.51 or more is ineligible for the enhanced rate. + nc_sa_eligible: false nc_sa: 0 -- name: Case 33, enhanced in-home exact income limit also prevents partial payment. - period: 2026-01 - absolute_error_margin: 0.01 - input: - state_code: NC - age: 65 - social_security_retirement: 22_578.12 - nc_sa_care_setting: IN_HOME_ENHANCED - nc_sa_days_eligible: 15 - output: - nc_sa: 0 - -- name: Case 34, last eligible cent yields one dollar for a full basic month. +# SA-3200 I: "The budget unit is always one", so a non-SSI applicant's own +# resources are tested against $2,000; SSI's $3,000 couple test does not apply. +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=61 +- name: Case 36, married non-SSI in-home applicant is tested on own resources and income only. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 75 + social_security_retirement: 13_200 + bank_account_assets: 1_800 + nc_sa_care_setting: IN_HOME + person2: + age: 72 + social_security_retirement: 10_800 + bank_account_assets: 1_800 + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NC + output: + # Joint SSI claim: resources 1,800 + 1,800 = 3,600 > 3,000, and income + # 1,100 + 900 - 20 = 1,980 > 1,491, so neither spouse receives SSI. + ssi: [0, 0] + # Own income only: 1,100 - 20 = 1,080 and 900 - 20 = 880. + nc_sa_countable_income: [1_080, 880] + # person1: aged, citizen, own resources 1,800 <= 2,000, TCMI 1,080 < 1,466.51. + # person2 has no approved care setting. + nc_sa_eligible: [true, false] + # 1,397 + 70 - 1,080 = 387. + nc_sa: [387, 0] + +# SA-3220 II.A and IV.A: a lower facility rate replaces the basic rate in +# the per diem, and initial-month income is not deducted. +# PDF pages 204, 209-210 +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204 +- name: Case 37, lower contracted facility rate in a fifteen-day initial month. period: 2026-01 absolute_error_margin: 0.01 input: state_code: NC age: 65 - social_security_retirement: 17_838 - nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_days_eligible: 31 - output: - nc_sa: 1 - -# The earned-income exclusion halves the remaining amount; preserve fractional cents. -# Monthly earnings 3018.01 less 20 and 65, divided by 2, give 1466.505 < 1466.51. -- name: Case 35, a half-cent countable earned-income amount below the limit remains eligible. - period: 2026-01 - absolute_error_margin: 0.01 - input: - state_code: NC - age: 65 - employment_income: 36_216.12 nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_facility_rate: 1_000 nc_sa_days_eligible: 15 output: - nc_sa: 746 + # SSI 994 and TCMI 994 < 1,466.51, so the applicant is eligible. + nc_sa_eligible: true + # Per diem 1,000 / 31 = 32.2581 -> 32.26; 32.26 * 15 + 70 = 553.90 -> 554. + nc_sa: 554 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.yaml new file mode 100644 index 00000000000..9d9c6edd52a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.yaml @@ -0,0 +1,80 @@ +# SA-5400 III: SAIH payments are excluded as income in HUD housing programs +# (24 CFR 5.609(b)(12)(ii)); facility Special Assistance remains countable. +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=47 +- name: Case 1, basic adult care home payment counts for HUD. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + nc_sa_care_setting: ADULT_CARE_HOME + output: + # 1,397 + 70 - 994 = 473. + nc_sa: 473 + nc_sa_countable_for_hud: 473 + +- name: Case 2, special care unit payment counts for HUD. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + nc_sa_care_setting: SPECIAL_CARE_UNIT + output: + # 1,792 + 70 - 994 = 868. + nc_sa: 868 + nc_sa_countable_for_hud: 868 + +- name: Case 3, basic in-home payment is excluded for HUD. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + nc_sa_care_setting: IN_HOME + output: + nc_sa: 473 + nc_sa_countable_for_hud: 0 + +- name: Case 4, enhanced in-home payment is excluded for HUD. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + nc_sa_care_setting: IN_HOME_ENHANCED + output: + nc_sa: 868 + nc_sa_countable_for_hud: 0 + +# Disabled-worker benefit of $711 a month, the December 2024 North Carolina +# average for disabled workers who also receive SSI ($710.97, SSA SSI Annual +# Statistical Report 2024, Table 16). +# https://www.ssa.gov/policy/docs/statcomps/ssi_asr/2024/sect03.html +- name: Case 5, annual HUD unearned income includes facility Special Assistance. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 55 + meets_ssi_disability_criteria: true + social_security_disability: 8_532 + nc_sa_care_setting: ADULT_CARE_HOME + output: + # SSI 994 - (711 - 20) = 303 a month; 303 * 12 = 3,636. + ssi: 3_636 + # TCMI 691 + 303 = 994; (1,397 + 70 - 994) * 12 = 473 * 12 = 5,676. + nc_sa: 5_676 + nc_sa_countable_for_hud: 5_676 + # Social Security 8,532 + SSI 3,636 + SA 5,676 = 17,844. + hud_unearned_income: 17_844 + +- name: Case 6, outside North Carolina. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VA + age: 70 + nc_sa_care_setting: ADULT_CARE_HOME + output: + nc_sa_countable_for_hud: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_income.yaml new file mode 100644 index 00000000000..9cde463dbb4 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_income.yaml @@ -0,0 +1,110 @@ +# Retired-worker benefit of $2,088 a month, the August 2026 average +# ($2,087.52, SSA Monthly Statistical Snapshot, Table 2). +# https://www.ssa.gov/policy/docs/quickfacts/stat_snapshot/ +- name: Case 1, Social Security less the general income exclusion with no SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + social_security_retirement: 25_056 + nc_sa_care_setting: ADULT_CARE_HOME + output: + # 25,056 / 12 = 2,088; 2,088 - 20 = 2,068 > 994, so no SSI. + ssi: 0 + # 2,068 + SSI 0; the facility restoration adds max(994 - 2,068, 0) = 0. + nc_sa_countable_income: 2_068 + +# Disabled-worker benefit of $1,636 a month, the August 2026 average +# ($1,635.90, SSA Monthly Statistical Snapshot, Table 2), plus 20 hours a +# week at North Carolina's $7.25 minimum wage: 7.25 * 20 * 52 = $7,540. +- name: Case 2, earned income after the $65 and one-half exclusions. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 45 + meets_ssi_disability_criteria: true + social_security_disability: 19_632 + employment_income: 7_540 + nc_sa_care_setting: IN_HOME + output: + # Unearned 1,636 - 20 = 1,616; earned (628.33 - 65) / 2 = 281.67. + # 1,616 + 281.67 = 1,897.67 > 994, so no SSI. + ssi: 0 + nc_sa_countable_income: 1_897.67 + +# SA-3210 V.D.1.e: SA budgets assume the full FBR unless SSA counts the +# applicant as part of a married couple or SSI is one-third reduced for an +# SAIH applicant. +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=195 +# Disabled-worker benefit of $711 a month, the December 2024 North Carolina +# average for disabled workers who also receive SSI ($710.97, SSA SSI Annual +# Statistical Report 2024, Table 16). +# https://www.ssa.gov/policy/docs/statcomps/ssi_asr/2024/sect03.html +- name: Case 3, individual facility budget restores one-third-reduced SSI to the full FBR. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 55 + meets_ssi_disability_criteria: true + social_security_disability: 8_532 + nc_sa_care_setting: ADULT_CARE_HOME + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + output: + # Reduced FBR 994 * (1 - 1/3) = 662.67 < 711 - 20 = 691, so no SSI. + ssi: 0 + # 691 + max(0, 994 - 691) = 691 + 303 = 994. + nc_sa_countable_income: 994 + +- name: Case 4, in-home budget keeps one-third-reduced SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 55 + meets_ssi_disability_criteria: true + social_security_disability: 8_532 + nc_sa_care_setting: IN_HOME + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + output: + ssi: 0 + # 711 - 20 = 691 + SSI 0; no restoration for in-home budgets. + nc_sa_countable_income: 691 + +- name: Case 5, joint SSI claim keeps the couple amount in an adult care home. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 58 + meets_ssi_disability_criteria: true + social_security_disability: 8_532 + nc_sa_care_setting: ADULT_CARE_HOME + person2: + age: 55 + meets_ssi_disability_criteria: true + social_security_disability: 8_532 + nc_sa_care_setting: ADULT_CARE_HOME + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NC + output: + # Joint claim: (711 + 711 - 20) / 2 = 701 countable each; + # couple FBR 1,491 / 2 = 745.50; 745.50 - 701 = 44.50. + ssi: [44.5, 44.5] + # 711 - 20 = 691 + SSI 44.50 = 735.50; no restoration to 994 for a couple. + nc_sa_countable_income: [735.5, 735.5] diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_eligible.yaml new file mode 100644 index 00000000000..e40568a01a1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_eligible.yaml @@ -0,0 +1,138 @@ +# SA-3100 II.B and SA-3200 I.A: an SSI recipient automatically meets the +# categorical, financial and resource requirements. +# PDF pages 7, 61 +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=7 +- name: Case 1, SSI recipient in an adult care home is eligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 72 + nc_sa_care_setting: ADULT_CARE_HOME + output: + # No other income: SSI 994; TCMI 994 < 1,466.51. + ssi: 994 + nc_sa_eligible: true + +- name: Case 2, reported SSI receipt waives the resource test when modeled SSI is zero. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 72 + nc_sa_care_setting: ADULT_CARE_HOME + bank_account_assets: 2_500 + receives_ssi: true + output: + # Modeled SSI fails the 2,000 resource limit (2,500 > 2,000). + ssi: 0 + # Reported receipt meets the resource requirement; the facility budget + # restores TCMI to 994 < 1,466.51. + nc_sa_eligible: true + +# SA-3200 I: "The resource limit is $2000 for an applicant/recipient. The +# budget unit is always one." +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=61 +- name: Case 3, non-SSI applicant with an ineligible spouse is tested on own resources only. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 75 + social_security_retirement: 13_200 + bank_account_assets: 1_800 + nc_sa_care_setting: IN_HOME + person2: + age: 60 + bank_account_assets: 3_500 + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NC + output: + # person1 SSI: 1,100 - 20 = 1,080 > 994, and the spouse's 3,500 + # counts against SSI's 3,000 limit; person2 is not aged, blind or disabled. + ssi: [0, 0] + # person1: own resources 1,800 <= 2,000 and TCMI 1,080 < 1,466.51. + nc_sa_eligible: [true, false] + +- name: Case 4, non-SSI applicant over the individual limit is ineligible although the couple limit is met. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 75 + social_security_retirement: 13_200 + bank_account_assets: 2_500 + nc_sa_care_setting: IN_HOME + person2: + age: 72 + social_security_retirement: 10_800 + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NC + output: + # Joint SSI claim: resources 2,500 + 0 = 2,500 <= 3,000. + is_ssi_eligible: [true, true] + # Joint income (1,100 + 900 - 20) / 2 = 990 > 745.50 each. + ssi: [0, 0] + # person1's own resources 2,500 > 2,000. + nc_sa_eligible: [false, false] + +- name: Case 5, non-SSI applicant with resources at the limit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + social_security_retirement: 15_600 + bank_account_assets: 2_000 + nc_sa_care_setting: ADULT_CARE_HOME + output: + # 1,300 - 20 = 1,280 > 994. + ssi: 0 + # Resources 2,000 <= 2,000 and TCMI 1,280 < 1,466.51. + nc_sa_eligible: true + +- name: Case 6, non-SSI applicant with resources one dollar over the limit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + social_security_retirement: 15_600 + bank_account_assets: 2_001 + nc_sa_care_setting: ADULT_CARE_HOME + output: + ssi: 0 + # Resources 2,001 > 2,000. + nc_sa_eligible: false + +# SA-3100 II.A.7: be a U.S. citizen or qualified alien. +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6 +- name: Case 7, applicant without a qualified immigration status is ineligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + immigration_status: UNDOCUMENTED + nc_sa_care_setting: ADULT_CARE_HOME + output: + ssi: 0 + nc_sa_eligible: false diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py index b503780ce02..15d13536d04 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py @@ -9,8 +9,10 @@ class nc_sa(Variable): unit = USD defined_for = "nc_sa_eligible" reference = ( - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + # PDF pages 207-210 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207", + # PDF pages 42-43 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=42", ) def formula(person, period, parameters): diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py index ee711491464..d4e024d72bd 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py @@ -2,11 +2,11 @@ class NCSACareSetting(Enum): - NONE = "No approved Special Assistance care setting" ADULT_CARE_HOME = "Approved basic residential care" SPECIAL_CARE_UNIT = "Approved enhanced special care unit" IN_HOME = "Approved basic in-home care" IN_HOME_ENHANCED = "Approved enhanced in-home care" + NONE = "No approved Special Assistance care setting" class nc_sa_care_setting(Variable): @@ -18,6 +18,8 @@ class nc_sa_care_setting(Variable): default_value = NCSACareSetting.NONE documentation = "A positive category indicates an approved placement or in-home service plan with a valid FL-2, satisfaction of the state residency waiting period or exception, pursuit of other benefits, and no conflicting PACE or waiver enrollment. Enhanced settings require the qualifying dementia diagnosis. A blind child requires a certified specialized community residential center." reference = ( - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + # PDF pages 6-7, 10, 204-205 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6", + # PDF pages 14-17 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=14", ) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py index 7327cc1c9e8..6d3326c0e86 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py @@ -4,10 +4,15 @@ class nc_sa_countable_for_hud(Variable): value_type = float entity = Person - label = "North Carolina State Assistance countable for HUD" + label = "North Carolina Special Assistance countable for HUD" definition_period = MONTH unit = USD - reference = "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf" + defined_for = StateCode.NC + reference = ( + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=47", + # 24 CFR 5.609(b)(12)(ii) + "https://www.law.cornell.edu/cfr/text/24/5.609", + ) def formula(person, period, parameters): category = person("nc_sa_care_setting", period) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py index 45523f343a1..f05caa2a49f 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py @@ -10,7 +10,9 @@ class nc_sa_countable_income(Variable): label = "North Carolina SA countable income" definition_period = MONTH unit = USD - reference = "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf" + defined_for = StateCode.NC + # PDF pages 185-196, 204 + reference = "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=185" def formula(person, period, parameters): # SA is always a one-person budget: no spousal or parental deeming. diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py index cdb1f3d16ff..d8475bcacf0 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py @@ -6,10 +6,13 @@ class nc_sa_days_eligible(Variable): entity = Person label = "North Carolina SA eligible days in initial month" definition_period = MONTH + defined_for = StateCode.NC documentation = "For an initial partial month, days from the date all eligibility conditions are satisfied through month end, including the first eligible day. Ongoing months default to the full month. This does not implement a discharge-month settlement." reference = ( - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + # PDF pages 209-210 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=209", + # PDF pages 42-43 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=42", ) def formula(person, period, parameters): diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py index c21332511d1..90587dc448e 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py @@ -8,8 +8,10 @@ class nc_sa_eligible(Variable): definition_period = MONTH defined_for = StateCode.NC reference = ( - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + # PDF pages 6-7, 61, 207, 209 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6", + # PDF pages 14, 18, 41 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=14", ) def formula(person, period, parameters): @@ -20,35 +22,41 @@ def formula(person, period, parameters): in_home = (setting == settings.IN_HOME) | (setting == settings.IN_HOME_ENHANCED) adult = age >= p.minimum_adult_age age_eligible = adult | (person("is_blind", period.this_year) & ~in_home) - receives_ssi = person("ssi", period) > 0 - # SSI receipt establishes the resource test. Non-SSI cases use an - # individual state budget, in addition to SSI non-income eligibility. + receives_ssi = (person("ssi", period) > 0) | person("receives_ssi", period) + # An SSI recipient automatically meets the categorical, financial and + # resource requirements (SA-3100 II.B; SA-3200 I.A). A non-SSI + # applicant must meet SSI's non-income requirements, but SA tests only + # the applicant's own resources: "The budget unit is always one" + # (SA-3200 I), so SSI's couple and spousal resource rules do not apply. + aged_blind_disabled = person("is_ssi_aged_blind_disabled", period.this_year) + immigration_status = person("immigration_status", period.this_year) + citizen = immigration_status == immigration_status.possible_values.CITIZEN + immigration_eligible = citizen | person( + "is_ssi_qualified_noncitizen", period.this_year + ) resource_limit = parameters( period ).gov.ssa.ssi.eligibility.resources.limit.individual resources = person("ssi_countable_resources", period.this_year) - resources_eligible = receives_ssi | (resources <= resource_limit) - ssi_status = receives_ssi | person("is_ssi_eligible", period) + non_ssi_eligible = ( + aged_blind_disabled & immigration_eligible & (resources <= resource_limit) + ) enhanced = (setting == settings.SPECIAL_CARE_UNIT) | ( setting == settings.IN_HOME_ENHANCED ) income_limit = where(enhanced, p.income_limit.enhanced, p.income_limit.basic) # The monthly income test still applies when the first payment is - # prorated without subtracting income (SA-3220 II.E and III). + # prorated without subtracting income (SA-3220 II.E and IV.A). countable_income = person("nc_sa_countable_income", period) - # Annual-to-month arithmetic can put an exact cutoff one storage - # step below it. Preserve sub-cent income; do not round the budget. - comparison_limit = np.nextafter( - income_limit.astype(countable_income.dtype), - -np.inf, - dtype=countable_income.dtype, - ) - income_eligible = countable_income < comparison_limit + # TCMI and the cutoff are amounts in cents (ineligible at $1,466.51 or + # more), so compare whole cents rather than the float values that + # annual-to-month arithmetic produces. + income_cents = np.floor(countable_income.astype(np.float64) * 100 + 0.5) + limit_cents = np.floor(income_limit * 100 + 0.5) return ( (setting != settings.NONE) & age_eligible - & resources_eligible - & ssi_status - & income_eligible + & (receives_ssi | non_ssi_eligible) + & (income_cents < limit_cents) & person("takes_up_ssi_if_eligible", period.this_year) ) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py index c63e51bd116..06e67c81d86 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py +++ b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py @@ -7,10 +7,12 @@ class nc_sa_facility_rate(Variable): label = "North Carolina SA contracted monthly care rate" definition_period = MONTH unit = USD + defined_for = StateCode.NC documentation = "Defaults to the published rate. Supply the actual rate if a facility contracts for less. In-home payments use the published basic or enhanced rate." reference = ( - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf", - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf", + # PDF pages 204-206 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204", + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=40", ) def formula(person, period, parameters): 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 356cd7faee6..f9c9e196716 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", - "nc_sa", + "nc_sa", # North Carolina benefits "ks_sspp", # Kansas benefits "hi_oss", "la_oss", # Louisiana benefits From 4ddc9420cd3294ce31d0e2dc36b11a1af8c3a24e Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 15 Oct 2026 14:49:59 -0400 Subject: [PATCH 4/6] Address review: North Carolina Special Assistance rates, eligibility and LIEAP - Move the program to nc/ncdhhs/sa next to lieap, scca and tanf. - Uprate the basic and enhanced rates by the Social Security COLA (G.S. 108A-42.1) and derive eligibility from SA-3220 III.C at the rate actually used, replacing the stored income limits. - Make nc_sa_facility_rate and nc_sa_partial_month_days plain inputs (0 = published rate / full month) so one person's input no longer resets other residents; add nc_sa_rate and nc_sa_full_month_amount. - Require SSI eligibility unless the applicant is over income for SSI (SA-3100 I, II.A.6; SA-3110 V.K), add the inmate gate, and document spouses living apart as separate marital units. - Count nc_sa as unearned income for North Carolina LIEAP (FNS 300.02, SA-5400 II). - Descriptions, references, case names and new cases. Co-Authored-By: Claude Opus 5.5 --- changelog.d/nc-ssp-2026.added.md | 2 +- .../states/nc/dhhs/sa/income_limit/basic.yaml | 13 -- .../nc/dhhs/sa/income_limit/enhanced.yaml | 13 -- .../states/nc/dhhs/sa/minimum_adult_age.yaml | 13 -- .../gov/states/nc/dhhs/sa/rate/basic.yaml | 13 -- .../gov/states/nc/dhhs/sa/rate/enhanced.yaml | 13 -- .../ncdhhs/lieap/unearned_income_sources.yaml | 6 + .../nc/ncdhhs/sa/minimum_adult_age.yaml | 13 ++ .../sa/personal_needs_allowance.yaml | 2 +- .../gov/states/nc/ncdhhs/sa/rate/basic.yaml | 22 +++ .../states/nc/ncdhhs/sa/rate/enhanced.yaml | 22 +++ policyengine_us/programs.yaml | 4 +- .../states/nc/ncdhhs/lieap/integration.yaml | 25 +++ .../nc/{dhhs => ncdhhs}/sa/integration.yaml | 90 +++++++++- .../states/nc/{dhhs => ncdhhs}/sa/nc_sa.yaml | 162 +++++++++++++++--- .../sa/nc_sa_countable_for_hud.yaml | 0 .../sa/nc_sa_countable_income.yaml | 4 +- .../{dhhs => ncdhhs}/sa/nc_sa_eligible.yaml | 52 ++++++ .../gov/states/nc/ncdhhs/sa/nc_sa_rate.yaml | 78 +++++++++ .../variables/gov/states/nc/dhhs/sa/nc_sa.py | 39 ----- .../gov/states/nc/dhhs/sa/nc_sa_eligible.py | 62 ------- .../states/nc/dhhs/sa/nc_sa_facility_rate.py | 25 --- .../gov/states/nc/ncdhhs/sa/nc_sa.py | 32 ++++ .../{dhhs => ncdhhs}/sa/nc_sa_care_setting.py | 2 +- .../sa/nc_sa_countable_for_hud.py | 0 .../sa/nc_sa_countable_income.py | 10 +- .../gov/states/nc/ncdhhs/sa/nc_sa_eligible.py | 84 +++++++++ .../nc/ncdhhs/sa/nc_sa_facility_rate.py | 16 ++ .../nc/ncdhhs/sa/nc_sa_full_month_amount.py | 27 +++ .../sa/nc_sa_partial_month_days.py} | 10 +- .../gov/states/nc/ncdhhs/sa/nc_sa_rate.py | 32 ++++ 31 files changed, 653 insertions(+), 233 deletions(-) delete mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml delete mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml delete mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml delete mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml delete mode 100644 policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml create mode 100644 policyengine_us/parameters/gov/states/nc/ncdhhs/sa/minimum_adult_age.yaml rename policyengine_us/parameters/gov/states/nc/{dhhs => ncdhhs}/sa/personal_needs_allowance.yaml (69%) create mode 100644 policyengine_us/parameters/gov/states/nc/ncdhhs/sa/rate/basic.yaml create mode 100644 policyengine_us/parameters/gov/states/nc/ncdhhs/sa/rate/enhanced.yaml rename policyengine_us/tests/policy/baseline/gov/states/nc/{dhhs => ncdhhs}/sa/integration.yaml (64%) rename policyengine_us/tests/policy/baseline/gov/states/nc/{dhhs => ncdhhs}/sa/nc_sa.yaml (66%) rename policyengine_us/tests/policy/baseline/gov/states/nc/{dhhs => ncdhhs}/sa/nc_sa_countable_for_hud.yaml (100%) rename policyengine_us/tests/policy/baseline/gov/states/nc/{dhhs => ncdhhs}/sa/nc_sa_countable_income.yaml (94%) rename policyengine_us/tests/policy/baseline/gov/states/nc/{dhhs => ncdhhs}/sa/nc_sa_eligible.yaml (70%) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_rate.yaml delete mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py delete mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py delete mode 100644 policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py create mode 100644 policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa.py rename policyengine_us/variables/gov/states/nc/{dhhs => ncdhhs}/sa/nc_sa_care_setting.py (79%) rename policyengine_us/variables/gov/states/nc/{dhhs => ncdhhs}/sa/nc_sa_countable_for_hud.py (100%) rename policyengine_us/variables/gov/states/nc/{dhhs => ncdhhs}/sa/nc_sa_countable_income.py (75%) create mode 100644 policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_eligible.py create mode 100644 policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_facility_rate.py create mode 100644 policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_full_month_amount.py rename policyengine_us/variables/gov/states/nc/{dhhs/sa/nc_sa_days_eligible.py => ncdhhs/sa/nc_sa_partial_month_days.py} (51%) create mode 100644 policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_rate.py diff --git a/changelog.d/nc-ssp-2026.added.md b/changelog.d/nc-ssp-2026.added.md index 5b3aeacd07f..a165a54b001 100644 --- a/changelog.d/nc-ssp-2026.added.md +++ b/changelog.d/nc-ssp-2026.added.md @@ -1 +1 @@ -Add North Carolina State and County Special Assistance for 2026. +Add North Carolina State-County Special Assistance for 2026. diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml deleted file mode 100644 index 1ed1da8973b..00000000000 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/basic.yaml +++ /dev/null @@ -1,13 +0,0 @@ -description: North Carolina requires monthly countable income below this threshold for the basic rate under its Special Assistance program. -values: - 2026-01-01: 1_466.51 - -metadata: - unit: currency-USD - period: month - label: North Carolina SA basic exclusive income limit - reference: - - title: North Carolina Special Assistance Manual SA-3220 II.E.3 - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207 - - title: North Carolina Special Assistance In-Home Program Manual SA-5300 IV.A.3 - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=41 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml deleted file mode 100644 index 8c98c4ac873..00000000000 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/income_limit/enhanced.yaml +++ /dev/null @@ -1,13 +0,0 @@ -description: North Carolina requires monthly countable income below this threshold for the enhanced rate under its Special Assistance program. -values: - 2026-01-01: 1_861.51 - -metadata: - unit: currency-USD - period: month - label: North Carolina SA enhanced exclusive income limit - reference: - - title: North Carolina Special Assistance Manual SA-3220 II.E.4 - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207 - - title: North Carolina Special Assistance In-Home Program Manual SA-5300 IV.A.4 - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=41 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml deleted file mode 100644 index c9df4896683..00000000000 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/minimum_adult_age.yaml +++ /dev/null @@ -1,13 +0,0 @@ -description: North Carolina requires disabled Special Assistance applicants and in-home participants to be at least this age. -values: - 2026-01-01: 18 - -metadata: - unit: year - period: year - label: North Carolina SA minimum adult age - reference: - - title: "North Carolina Special Assistance Manual SA-3100 II.A.1 and III.C #page 6,10" - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6 - - title: North Carolina Special Assistance In-Home Program Manual SA-5200 - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=14 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml deleted file mode 100644 index 715ffa1e838..00000000000 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/basic.yaml +++ /dev/null @@ -1,13 +0,0 @@ -description: North Carolina pays up to this monthly basic rate before the personal needs allowance and income deductions under its Special Assistance program. -values: - 2026-01-01: 1_397 - -metadata: - unit: currency-USD - period: month - label: North Carolina SA basic rate - reference: - - title: "North Carolina Special Assistance Manual SA-3220 II.A and VIII #page 204,219" - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204 - - title: North Carolina Change Notice EFS-SA-CN-01-2026 I - href: https://policies.ncdhhs.gov/wp-content/uploads/EFS-SA-CN-01-2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml b/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml deleted file mode 100644 index 62bbd3bbf2b..00000000000 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/rate/enhanced.yaml +++ /dev/null @@ -1,13 +0,0 @@ -description: North Carolina pays up to this monthly enhanced rate before the personal needs allowance and income deductions under its Special Assistance program. -values: - 2026-01-01: 1_792 - -metadata: - unit: currency-USD - period: month - label: North Carolina SA enhanced rate - reference: - - title: "North Carolina Special Assistance Manual SA-3220 II.B and VIII #page 205,219" - href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=205 - - title: North Carolina Change Notice EFS-SA-CN-01-2026 I - href: https://policies.ncdhhs.gov/wp-content/uploads/EFS-SA-CN-01-2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/nc/ncdhhs/lieap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/nc/ncdhhs/lieap/unearned_income_sources.yaml index da067718336..15e8b92829a 100644 --- a/policyengine_us/parameters/gov/states/nc/ncdhhs/lieap/unearned_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/nc/ncdhhs/lieap/unearned_income_sources.yaml @@ -10,6 +10,10 @@ values: # "Social Security Benefits" (page 20). The plan checks "Including # MediCare deduction", so the benefit is counted gross. - social_security + # "Special Assistance (SA)" (page 20): facility SA, SA In-Home and SA for + # the Certain Disabled. SA-5400 II also counts the SA In-Home payment for + # Energy Programs. + - nc_sa # "General Assistance (GA)" (page 9); plan "General Assistance benefits". - general_assistance # "Pensions" (page 16); plan "Retirement / pension benefits". @@ -53,3 +57,5 @@ metadata: href: https://policies.ncdhhs.gov/wp-content/uploads/fns-300-sources-of-income.pdf#page=1 - title: North Carolina FY2026 state plan, item 1.9 countable income checklist, pages 6-7 href: https://liheapch.acf.gov/docs/2026/state-plans/NC_Plan_2026.pdf#page=6 + - title: North Carolina Special Assistance In-Home Program Manual, SA-5400 II + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=47 diff --git a/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/minimum_adult_age.yaml b/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/minimum_adult_age.yaml new file mode 100644 index 00000000000..d05cfe52d12 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/minimum_adult_age.yaml @@ -0,0 +1,13 @@ +description: North Carolina sets this age as the minimum for disabled and in-home applicants under the State-County Special Assistance program. +values: + 2026-01-01: 18 + +metadata: + unit: year + period: year + label: North Carolina SA minimum adult age + reference: + - title: North Carolina General Statutes § 108A-41(b)(1) + href: https://www.ncleg.gov/EnactedLegislation/Statutes/PDF/BySection/Chapter_108A/GS_108A-41.pdf#page=1 + - title: "North Carolina Special Assistance Manual SA-3100 II.A.1 and III.C #page 6,10" + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/personal_needs_allowance.yaml similarity index 69% rename from policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml rename to policyengine_us/parameters/gov/states/nc/ncdhhs/sa/personal_needs_allowance.yaml index 514e2619b8d..fb6d865f383 100644 --- a/policyengine_us/parameters/gov/states/nc/dhhs/sa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/personal_needs_allowance.yaml @@ -1,4 +1,4 @@ -description: North Carolina provides this monthly personal needs allowance under its Special Assistance program. +description: North Carolina provides this amount as the monthly personal needs allowance under the State-County Special Assistance program. values: 2022-01-01: 70 diff --git a/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/rate/basic.yaml b/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/rate/basic.yaml new file mode 100644 index 00000000000..ab22c2f1430 --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/rate/basic.yaml @@ -0,0 +1,22 @@ +description: North Carolina provides this amount as the maximum monthly basic rate under the State-County Special Assistance program. +values: + 2026-01-01: 1_397 + +metadata: + unit: currency-USD + period: month + label: North Carolina SA basic rate + # G.S. 108A-42.1(a) adjusts the rate each January 1 by the Social Security + # cost-of-living adjustment; the published rate replaces the projection. + uprating: + parameter: gov.ssa.uprating + rounding: + type: nearest + interval: 1 + reference: + - title: North Carolina General Statutes § 108A-42.1(a) + href: https://www.ncleg.gov/EnactedLegislation/Statutes/PDF/BySection/Chapter_108A/GS_108A-42.1.pdf + - title: "North Carolina Special Assistance Manual SA-3220 II.A and VIII #page 204,219" + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204 + - title: North Carolina Change Notice EFS-SA-CN-01-2026 I + href: https://policies.ncdhhs.gov/wp-content/uploads/EFS-SA-CN-01-2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/rate/enhanced.yaml b/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/rate/enhanced.yaml new file mode 100644 index 00000000000..6bd404365dc --- /dev/null +++ b/policyengine_us/parameters/gov/states/nc/ncdhhs/sa/rate/enhanced.yaml @@ -0,0 +1,22 @@ +description: North Carolina provides this amount as the maximum monthly enhanced rate for special care units and in-home recipients with Alzheimer's disease or dementia under the State-County Special Assistance program. +values: + 2026-01-01: 1_792 + +metadata: + unit: currency-USD + period: month + label: North Carolina SA enhanced rate + # G.S. 108A-42.1(b) adjusts the rate each January 1 by the Social Security + # cost-of-living adjustment; the published rate replaces the projection. + uprating: + parameter: gov.ssa.uprating + rounding: + type: nearest + interval: 1 + reference: + - title: North Carolina General Statutes § 108A-42.1(b) + href: https://www.ncleg.gov/EnactedLegislation/Statutes/PDF/BySection/Chapter_108A/GS_108A-42.1.pdf + - title: "North Carolina Special Assistance Manual SA-3220 II.B and VIII #page 205,219" + href: https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=205 + - title: North Carolina Change Notice EFS-SA-CN-01-2026 I + href: https://policies.ncdhhs.gov/wp-content/uploads/EFS-SA-CN-01-2026.pdf#page=1 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index d042f462db9..6cadc95cb0a 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1093,9 +1093,9 @@ programs: - state: NC status: partial name: North Carolina SA - full_name: North Carolina State and County Special Assistance + full_name: North Carolina State-County Special Assistance variable: nc_sa - parameter_prefix: gov.states.nc.dhhs.sa + parameter_prefix: gov.states.nc.ncdhhs.sa verified_years: '2026' notes: Models 2026 basic and enhanced facility and in-home payments, including lower contracted rates and first-month proration. Open/shut and discharge payments are not modeled. diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/lieap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/lieap/integration.yaml index e1995f37483..4cea8f7d21f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/lieap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/lieap/integration.yaml @@ -333,3 +333,28 @@ has_heating_expense: [true, true, true, true] nc_lieap_eligible: [true, false, true, false] nc_lieap: [400, 0, 300, 0] + +- name: Case 10, Special Assistance In-Home counts as unearned income. + # FNS 300.02 "Special Assistance (SA)" is countable unearned income (page + # 20), and SA-5400 II counts the SA In-Home payment for Energy Programs. + # Age 65, no other income: SSI 994 x 12 = 11_928; SA In-Home basic + # (1,397 + 70 - 994) x 12 = 5_676. Gross 17_604; medical deduction 1_020 -> + # nc_lieap_income 16_584 ($1,382 a month). Limit 15_650 x 1.5 = 23_475 -> + # $1,956. Lower band ends at $978 -> $300. Without SA the income would be + # 10_908 ($909) and the payment $400. + period: 2026 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME + tanf: 0 + childcare_expenses: 0 + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + output: + nc_sa: 5_676 + nc_lieap_gross_income_person: 17_604 + nc_lieap_income: 16_584 + nc_lieap_income_limit: 23_475 + nc_lieap_eligible: true + nc_lieap: 300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/integration.yaml similarity index 64% rename from policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml rename to policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/integration.yaml index 949549ece93..45d56a04f07 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/integration.yaml @@ -27,7 +27,7 @@ # Both receive SSI; TCMI 745.50 < 1,466.51. nc_sa_eligible: [true, true] # In-home basic payments use the published basic rate. - nc_sa_facility_rate: [1_397, 1_397] + nc_sa_rate: [1_397, 1_397] # 1,397 + 70 - 745.50 = 721.50 -> 722. nc_sa: [722, 722] @@ -60,7 +60,7 @@ nc_sa_countable_income: [155.5, 1_335.5] # person2 has no approved care setting. nc_sa_eligible: [true, false] - nc_sa_facility_rate: [1_397, 1_397] + nc_sa_rate: [1_397, 1_397] # 1,397 + 70 - 155.50 = 1,311.50 -> 1,312. nc_sa: [1_312, 0] @@ -97,7 +97,7 @@ nc_sa_countable_income: [0, 1_851.17] # person1: aged citizen with own resources 0 <= 2,000 and TCMI 0. nc_sa_eligible: [true, false] - nc_sa_facility_rate: [1_397, 1_397] + nc_sa_rate: [1_397, 1_397] # 1,397 + 70 - 0 = 1,467. nc_sa: [1_467, 0] @@ -116,12 +116,14 @@ nc_sa_eligible: 2026-01: true # 1,397 * 12 = 16,764. - nc_sa_facility_rate: 16_764 + nc_sa_rate: 16_764 # (1,397 + 70 - 994) * 12 = 473 * 12 = 5,676. nc_sa: 5_676 household_state_benefits: 5_676 # SSI 11,928 + SA 5,676 = 17,604. snap_unearned_income: 17_604 + # SSI 11,928 + SA 5,676 + the SNAP minimum benefit (24 * 9 + 25 * 3 = 291). + spm_unit_benefits: 17_895 - name: Case 5, in home household reduction exception. period: 2026-01 @@ -140,11 +142,11 @@ # In-home budgets keep the reduced SSI (SA-3210 V.D.1.e(3)): 0 + 662.67. nc_sa_countable_income: 662.67 nc_sa_eligible: true - nc_sa_facility_rate: 1_397 + nc_sa_rate: 1_397 # 1,397 + 70 - 662.67 = 804.33 -> 804. nc_sa: 804 -- name: Case 6, individual facility budget restores the federal rate. +- name: Case 6, ongoing individual facility month after the two-month admission window budgets the full federal rate. period: 2026-01 absolute_error_margin: 0.01 input: @@ -161,7 +163,7 @@ # Individual facility budget uses max(662.67, 994 - 0) = 994. nc_sa_countable_income: 994 nc_sa_eligible: true - nc_sa_facility_rate: 1_397 + nc_sa_rate: 1_397 # 1,397 + 70 - 994 = 473. nc_sa: 473 @@ -178,7 +180,7 @@ nc_sa_countable_income: 11_928 nc_sa_eligible: 2026-01: true - nc_sa_facility_rate: 16_764 + nc_sa_rate: 16_764 # 473 * 12 = 5,676. nc_sa: 5_676 # SAIH payments are excluded for HUD (SA-5400 III). @@ -187,3 +189,75 @@ snap_unearned_income: 17_604 # SSI 11,928 + SA 0 = 11,928. hud_unearned_income: 11_928 + +# SSA treats spouses as individuals once they no longer live in the same +# household on the first day of the month (20 CFR 416.1801(c); POMS SI +# 00501.154 A.1), so a facility resident whose spouse stays at home is entered +# in a separate marital unit after the month of entry. SA then budgets the +# resident at the individual FBR (SA-3210 V.D.1.e). +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=195 +- name: Case 8, adult care home resident whose spouse stays at home after the month of entry. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + nc_sa_care_setting: ADULT_CARE_HOME + person2: + age: 68 + marital_units: + resident: + members: [person1] + spouse: + members: [person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NC + output: + # Separate claims with no income: the individual FBR of 994 each. + ssi: [994, 994] + nc_sa_countable_income: [994, 994] + # 1,397 + 70 - 994 = 473; person2 has no approved care setting. + nc_sa: [473, 0] + +# A married SAIH applicant whose couple income is at or above the couple FBR +# need not apply for SSI (SA-3210 V.E.4.c), and SA is computed on countable +# income when SSI is denied for spousal income (SA-5400 V). The SSI take-up +# input therefore does not matter. +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=198 +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=48 +# person2 earns North Carolina's May 2023 median wage, as in Case 3. +- name: Case 9, applicant denied SSI for spousal income does not need SSI take-up. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + nc_sa_care_setting: IN_HOME + takes_up_ssi_if_eligible: false + person2: + age: 40 + employment_income: 45_448 + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NC + output: + # Deemed income leaves no SSI: (3,787.33 - 85) / 2 = 1,851.17 > 1,491. + uncapped_ssi: [-360.17, 0] + ssi: [0, 0] + nc_sa_eligible: [true, false] + # 1,397 + 70 - 0 = 1,467. + nc_sa: [1_467, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa.yaml similarity index 66% rename from policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml rename to policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa.yaml index c910a664b50..0e0b726a437 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa.yaml @@ -229,7 +229,7 @@ state_code: NC age: 65 nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_days_eligible: 27 + nc_sa_partial_month_days: 27 social_security_retirement: 15_600 output: nc_sa: 1_287 @@ -241,7 +241,7 @@ state_code: NC age: 65 nc_sa_care_setting: IN_HOME - nc_sa_days_eligible: 20 + nc_sa_partial_month_days: 20 output: nc_sa: 971 @@ -252,20 +252,44 @@ state_code: NC age: 65 nc_sa_care_setting: IN_HOME_ENHANCED - nc_sa_days_eligible: 14 + nc_sa_partial_month_days: 14 output: nc_sa: 879 -- name: Case 25, zero days. +# A lower rate or partial-month days entered for one resident leave the other +# resident, an unrelated adult, on the published rate and a full month. +- name: Case 25, contracted rate and partial month entered for one of two residents. period: 2026-01 absolute_error_margin: 0.01 input: - state_code: NC - age: 65 - nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_days_eligible: 0 + people: + person1: + age: 70 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_facility_rate: 1_000 + nc_sa_partial_month_days: 15 + person2: + age: 72 + nc_sa_care_setting: ADULT_CARE_HOME + marital_units: + unit1: + members: [person1] + unit2: + members: [person2] + tax_units: + unit1: + members: [person1] + unit2: + members: [person2] + households: + household: + members: [person1, person2] + state_code: NC output: - nc_sa: 0 + nc_sa_rate: [1_000, 1_397] + # person1: 1,000 / 31 = 32.26; 32.26 * 15 + 70 = 553.90 -> 554. + # person2: SSI 994; 1,397 + 70 - 994 = 473. + nc_sa: [554, 473] - name: Case 26, SSI opt out. period: 2026-01 @@ -278,7 +302,8 @@ output: nc_sa: 0 -# SA-3220 II.E and IV.A: strict countable-income ceilings apply before initial-month payment. +# SA-3220 II.E, III.C and IV.A: an applicant whose full-month payment rounds to +# $0 is ineligible, including in a prorated first month. # Cases 29-32 enter TCMI directly at the last eligible cent and at the cutoff. # https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207 - name: Case 27, income-ineligible basic entrant receives no partial-month grant. @@ -289,7 +314,7 @@ age: 65 social_security_retirement: 36_000 nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_days_eligible: 15 + nc_sa_partial_month_days: 15 output: nc_sa: 0 @@ -301,7 +326,7 @@ age: 65 social_security_retirement: 36_000 nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_days_eligible: 31 + nc_sa_partial_month_days: 31 output: nc_sa: 0 @@ -313,7 +338,7 @@ age: 65 nc_sa_countable_income: 1_466.5 nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_days_eligible: 15 + nc_sa_partial_month_days: 15 output: # TCMI 1,466.50 is below the 1,466.51 basic cutoff. nc_sa_eligible: true @@ -328,7 +353,7 @@ age: 65 nc_sa_countable_income: 1_466.51 nc_sa_care_setting: ADULT_CARE_HOME - nc_sa_days_eligible: 15 + nc_sa_partial_month_days: 15 output: # TCMI 1,466.51 or more is ineligible for the basic rate. nc_sa_eligible: false @@ -342,7 +367,7 @@ age: 65 nc_sa_countable_income: 1_861.5 nc_sa_care_setting: SPECIAL_CARE_UNIT - nc_sa_days_eligible: 15 + nc_sa_partial_month_days: 15 output: # TCMI 1,861.50 is below the 1,861.51 enhanced cutoff. nc_sa_eligible: true @@ -357,7 +382,7 @@ age: 65 nc_sa_countable_income: 1_861.51 nc_sa_care_setting: SPECIAL_CARE_UNIT - nc_sa_days_eligible: 15 + nc_sa_partial_month_days: 15 output: # TCMI 1,861.51 or more is ineligible for the enhanced rate. nc_sa_eligible: false @@ -366,7 +391,7 @@ # SA-3200 I: "The budget unit is always one", so a non-SSI applicant's own # resources are tested against $2,000; SSI's $3,000 couple test does not apply. # https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=61 -- name: Case 36, married non-SSI in-home applicant is tested on own resources and income only. +- name: Case 33, married non-SSI in-home applicant is tested on own resources and income only. period: 2026-01 absolute_error_margin: 0.01 input: @@ -406,7 +431,7 @@ # the per diem, and initial-month income is not deducted. # PDF pages 204, 209-210 # https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204 -- name: Case 37, lower contracted facility rate in a fifteen-day initial month. +- name: Case 34, lower contracted facility rate in a fifteen-day initial month. period: 2026-01 absolute_error_margin: 0.01 input: @@ -414,9 +439,106 @@ age: 65 nc_sa_care_setting: ADULT_CARE_HOME nc_sa_facility_rate: 1_000 - nc_sa_days_eligible: 15 + nc_sa_partial_month_days: 15 output: - # SSI 994 and TCMI 994 < 1,466.51, so the applicant is eligible. + # SSI 994; at the contracted rate 1,000 + 70 - 994 = 76 >= 1, so the + # applicant is eligible. nc_sa_eligible: true # Per diem 1,000 / 31 = 32.2581 -> 32.26; 32.26 * 15 + 70 = 553.90 -> 554. nc_sa: 554 + +# SA-3220 II.A: with a lower contracted rate, "the actual rate charged to the +# recipient ... is used in the calculations addressed in this chapter", +# including III.C's eligibility test. Social Security of $1,300 a month gives +# TCMI 1,300 - 20 = 1,280, above the 1,070 maintenance amount at a $1,000 rate. +# PDF pages 204, 209 +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204 +- name: Case 35, income above the lower contracted maintenance amount prevents a partial-month payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_facility_rate: 1_000 + nc_sa_partial_month_days: 15 + social_security_retirement: 15_600 + output: + nc_sa_countable_income: 1_280 + # 1,000 + 70 - 1,280 < 0. + nc_sa_full_month_amount: 0 + nc_sa_eligible: false + nc_sa: 0 + +- name: Case 36, income above the lower contracted maintenance amount in a full month. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_facility_rate: 1_000 + social_security_retirement: 15_600 + output: + nc_sa_full_month_amount: 0 + nc_sa_eligible: false + nc_sa: 0 + +# G.S. 108A-42.1 adjusts the rates each January 1 by the Social Security +# cost-of-living adjustment. The 2027 rate and FBR are projections. +# https://www.ncleg.gov/EnactedLegislation/Statutes/PDF/BySection/Chapter_108A/GS_108A-42.1.pdf +- name: Case 37, basic facility rate follows the cost-of-living adjustment in 2027. + period: 2027-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + output: + # 1,397 uprated by the projected Social Security index and rounded to the + # dollar. + nc_sa_rate: 1_436 + # Projected FBR. + ssi: 1_021.83 + # 1,436 + 70 - 1,021.83 = 484.17 -> 484. + nc_sa: 484 + +# An applicant "ineligible for SSI solely due to being over income for SSI" +# qualifies without applying for SSI (SA-3100 I, II.A.6): "If the a/b +# currently has income at or above the FBR, a referral to SSA to apply for SSI +# is not required" (SA-3210 V.A.4). +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=191 +- name: Case 38, applicant over income for SSI does not need SSI take-up. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + social_security_retirement: 15_600 + takes_up_ssi_if_eligible: false + output: + # 1,300 - 20 = 1,280 > 994, so no SSI. + ssi: 0 + nc_sa_eligible: true + # 1,397 + 70 - 1,280 = 187. + nc_sa: 187 + +# SA-3220 IV.A rounds the per diem to the cent and the total to the dollar, +# with half a dollar rounding up (III.A.4). +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=209 +- name: Case 39, partial month at a lower contracted rate ending in exactly fifty cents rounds up. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_facility_rate: + 2026-04: 1_059 + nc_sa_partial_month_days: + 2026-04: 25 + output: + # 1,059 / 30 = 35.30; 35.30 * 25 + 70 = 952.50 -> 953. + nc_sa: + 2026-04: 953 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_countable_for_hud.yaml similarity index 100% rename from policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.yaml rename to policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_countable_for_hud.yaml diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_countable_income.yaml similarity index 94% rename from policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_income.yaml rename to policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_countable_income.yaml index 9cde463dbb4..b26da4473e5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_countable_income.yaml @@ -42,7 +42,7 @@ # average for disabled workers who also receive SSI ($710.97, SSA SSI Annual # Statistical Report 2024, Table 16). # https://www.ssa.gov/policy/docs/statcomps/ssi_asr/2024/sect03.html -- name: Case 3, individual facility budget restores one-third-reduced SSI to the full FBR. +- name: Case 3, ongoing facility month after the two-month admission window restores one-third-reduced SSI to the full FBR. period: 2026-01 absolute_error_margin: 0.01 input: @@ -77,7 +77,7 @@ # 711 - 20 = 691 + SSI 0; no restoration for in-home budgets. nc_sa_countable_income: 691 -- name: Case 5, joint SSI claim keeps the couple amount in an adult care home. +- name: Case 5, couple sharing an adult care home room whom SSA treats as a couple keeps the couple amount. period: 2026-01 absolute_error_margin: 0.01 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_eligible.yaml similarity index 70% rename from policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_eligible.yaml rename to policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_eligible.yaml index e40568a01a1..4bd86188e2a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/dhhs/sa/nc_sa_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_eligible.yaml @@ -136,3 +136,55 @@ output: ssi: 0 nc_sa_eligible: false + +# SA-3100 II.A.5: the beneficiary must "Not be an inmate in a jail or prison". +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6 +- name: Case 8, incarcerated applicant is ineligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 72 + nc_sa_care_setting: ADULT_CARE_HOME + is_incarcerated: true + output: + nc_sa_eligible: false + nc_sa: 0 + +# An applicant whose income would qualify for SSI must be eligible for SSI; +# "If SSA denies the SSDI/SSI application ... based on eligibility criteria +# other than income (such as resources, transfers, fleeing felon, etc.), the +# SA application will also be denied" (SA-3110 V.K). SSI counts an ineligible +# spouse's resources (20 CFR 416.1202(a)). +# PDF pages 40-41 +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=40 +- name: Case 9, applicant denied SSI on a spouse's resources is ineligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 75 + bank_account_assets: 1_800 + nc_sa_care_setting: IN_HOME + person2: + age: 60 + bank_account_assets: 3_500 + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: NC + output: + # No income, so SSI would be payable, but 1,800 + 3,500 = 5,300 exceeds + # the 2,000 limit. + ssi: [0, 0] + # person1's own resources (1,800) would pass SA-3200 I, but SA requires + # SSI eligibility when the income would qualify. + nc_sa_eligible: [false, false] + nc_sa: [0, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_rate.yaml new file mode 100644 index 00000000000..ef5b484b3b9 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_rate.yaml @@ -0,0 +1,78 @@ +# SA-3220 II.A-B: the published basic and enhanced rates, or the lower rate a +# facility actually charges an SA resident. In-home payments use the published +# rate. +# PDF pages 204-205 +# https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204 +- name: Case 1, adult care home uses the published basic rate. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + output: + nc_sa_rate: 1_397 + +- name: Case 2, special care unit uses the published enhanced rate. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: SPECIAL_CARE_UNIT + output: + nc_sa_rate: 1_792 + +- name: Case 3, enhanced in-home care uses the published enhanced rate. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME_ENHANCED + output: + nc_sa_rate: 1_792 + +- name: Case 4, lower contracted adult care home rate. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_facility_rate: 1_000 + output: + nc_sa_rate: 1_000 + +- name: Case 5, contracted rate above the published basic rate is capped. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_facility_rate: 2_000 + output: + nc_sa_rate: 1_397 + +- name: Case 6, lower contracted special care unit rate. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: SPECIAL_CARE_UNIT + nc_sa_facility_rate: 1_500 + output: + nc_sa_rate: 1_500 + +- name: Case 7, in-home care ignores a contracted facility rate. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 65 + nc_sa_care_setting: IN_HOME + nc_sa_facility_rate: 1_000 + output: + nc_sa_rate: 1_397 diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py deleted file mode 100644 index 15d13536d04..00000000000 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa.py +++ /dev/null @@ -1,39 +0,0 @@ -from policyengine_us.model_api import * - - -class nc_sa(Variable): - value_type = float - entity = Person - label = "North Carolina State and County Special Assistance" - definition_period = MONTH - unit = USD - defined_for = "nc_sa_eligible" - reference = ( - # PDF pages 207-210 - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207", - # PDF pages 42-43 - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=42", - ) - - def formula(person, period, parameters): - p = parameters(period).gov.states.nc.dhhs.sa - setting = person("nc_sa_care_setting", period) - settings = setting.possible_values - enhanced = (setting == settings.SPECIAL_CARE_UNIT) | ( - setting == settings.IN_HOME_ENHANCED - ) - published_rate = where(enhanced, p.rate.enhanced, p.rate.basic) - in_home = (setting == settings.IN_HOME) | (setting == settings.IN_HOME_ENHANCED) - rate = where( - in_home, - published_rate, - min_(max_(person("nc_sa_facility_rate", period), 0), published_rate), - ) - income = person("nc_sa_countable_income", period) - full = np.floor(max_(rate + p.personal_needs_allowance - income, 0) + 0.5) - days = clip(person("nc_sa_days_eligible", period), 0, period.days) - # For the first partial month, income is not deducted and the full - # personal-needs allowance is added after prorating the facility rate. - daily = np.floor(rate.astype(np.float64) / period.days * 100 + 0.5) / 100 - partial = np.floor(daily * days + p.personal_needs_allowance + 0.5) - return (days > 0) * where(days == period.days, full, partial) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py deleted file mode 100644 index 90587dc448e..00000000000 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_eligible.py +++ /dev/null @@ -1,62 +0,0 @@ -from policyengine_us.model_api import * - - -class nc_sa_eligible(Variable): - value_type = bool - entity = Person - label = "North Carolina Special Assistance eligibility" - definition_period = MONTH - defined_for = StateCode.NC - reference = ( - # PDF pages 6-7, 61, 207, 209 - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6", - # PDF pages 14, 18, 41 - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=14", - ) - - def formula(person, period, parameters): - p = parameters(period).gov.states.nc.dhhs.sa - setting = person("nc_sa_care_setting", period) - settings = setting.possible_values - age = person("age", period.this_year) - in_home = (setting == settings.IN_HOME) | (setting == settings.IN_HOME_ENHANCED) - adult = age >= p.minimum_adult_age - age_eligible = adult | (person("is_blind", period.this_year) & ~in_home) - receives_ssi = (person("ssi", period) > 0) | person("receives_ssi", period) - # An SSI recipient automatically meets the categorical, financial and - # resource requirements (SA-3100 II.B; SA-3200 I.A). A non-SSI - # applicant must meet SSI's non-income requirements, but SA tests only - # the applicant's own resources: "The budget unit is always one" - # (SA-3200 I), so SSI's couple and spousal resource rules do not apply. - aged_blind_disabled = person("is_ssi_aged_blind_disabled", period.this_year) - immigration_status = person("immigration_status", period.this_year) - citizen = immigration_status == immigration_status.possible_values.CITIZEN - immigration_eligible = citizen | person( - "is_ssi_qualified_noncitizen", period.this_year - ) - resource_limit = parameters( - period - ).gov.ssa.ssi.eligibility.resources.limit.individual - resources = person("ssi_countable_resources", period.this_year) - non_ssi_eligible = ( - aged_blind_disabled & immigration_eligible & (resources <= resource_limit) - ) - enhanced = (setting == settings.SPECIAL_CARE_UNIT) | ( - setting == settings.IN_HOME_ENHANCED - ) - income_limit = where(enhanced, p.income_limit.enhanced, p.income_limit.basic) - # The monthly income test still applies when the first payment is - # prorated without subtracting income (SA-3220 II.E and IV.A). - countable_income = person("nc_sa_countable_income", period) - # TCMI and the cutoff are amounts in cents (ineligible at $1,466.51 or - # more), so compare whole cents rather than the float values that - # annual-to-month arithmetic produces. - income_cents = np.floor(countable_income.astype(np.float64) * 100 + 0.5) - limit_cents = np.floor(income_limit * 100 + 0.5) - return ( - (setting != settings.NONE) - & age_eligible - & (receives_ssi | non_ssi_eligible) - & (income_cents < limit_cents) - & person("takes_up_ssi_if_eligible", period.this_year) - ) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py b/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py deleted file mode 100644 index 06e67c81d86..00000000000 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_facility_rate.py +++ /dev/null @@ -1,25 +0,0 @@ -from policyengine_us.model_api import * - - -class nc_sa_facility_rate(Variable): - value_type = float - entity = Person - label = "North Carolina SA contracted monthly care rate" - definition_period = MONTH - unit = USD - defined_for = StateCode.NC - documentation = "Defaults to the published rate. Supply the actual rate if a facility contracts for less. In-home payments use the published basic or enhanced rate." - reference = ( - # PDF pages 204-206 - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204", - "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=40", - ) - - def formula(person, period, parameters): - p = parameters(period).gov.states.nc.dhhs.sa - setting = person("nc_sa_care_setting", period) - settings = setting.possible_values - enhanced = (setting == settings.SPECIAL_CARE_UNIT) | ( - setting == settings.IN_HOME_ENHANCED - ) - return where(enhanced, p.rate.enhanced, p.rate.basic) diff --git a/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa.py new file mode 100644 index 00000000000..17d2cb144e5 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa.py @@ -0,0 +1,32 @@ +from policyengine_us.model_api import * + + +class nc_sa(Variable): + value_type = float + entity = Person + label = "North Carolina State-County Special Assistance" + definition_period = MONTH + unit = USD + defined_for = "nc_sa_eligible" + reference = ( + # PDF pages 207-210 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207", + # PDF pages 42-43 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=42", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.nc.ncdhhs.sa + days = person("nc_sa_partial_month_days", period) + partial_month = (days > 0) & (days < period.days) + # For the first partial month, income is not deducted and the full + # personal-needs allowance is added after prorating the rate + # (SA-3220 IV.A). + # Work in whole cents so that a per diem or total ending in exactly + # half a cent or half a dollar rounds up. + rate = person("nc_sa_rate", period) + rate_cents = np.floor(rate.astype(np.float64) * 100 + 0.5) + daily_cents = np.floor(rate_cents / period.days + 0.5) + allowance_cents = np.floor(p.personal_needs_allowance * 100 + 0.5) + partial = np.floor((daily_cents * days + allowance_cents) / 100 + 0.5) + return where(partial_month, partial, person("nc_sa_full_month_amount", period)) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_care_setting.py similarity index 79% rename from policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py rename to policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_care_setting.py index d4e024d72bd..209c95efc85 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_care_setting.py +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_care_setting.py @@ -16,7 +16,7 @@ class nc_sa_care_setting(Variable): definition_period = MONTH possible_values = NCSACareSetting default_value = NCSACareSetting.NONE - documentation = "A positive category indicates an approved placement or in-home service plan with a valid FL-2, satisfaction of the state residency waiting period or exception, pursuit of other benefits, and no conflicting PACE or waiver enrollment. Enhanced settings require the qualifying dementia diagnosis. A blind child requires a certified specialized community residential center." + documentation = "A positive category indicates an approved placement or in-home service plan with a valid FL-2, satisfaction of the state residency waiting period or exception, pursuit of other benefits, and no conflicting PACE or waiver enrollment. Enhanced settings require the qualifying dementia diagnosis. A blind child requires a certified specialized community residential center. Put spouses who no longer live in the same household, such as a facility resident whose spouse stays at home, in separate marital units from the month after they stop living together, so that SSI and Special Assistance treat each spouse as an individual. Keep one marital unit for the month of entry and for a temporary absence." reference = ( # PDF pages 6-7, 10, 204-205 "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6", diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_countable_for_hud.py similarity index 100% rename from policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_for_hud.py rename to policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_countable_for_hud.py diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_countable_income.py similarity index 75% rename from policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py rename to policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_countable_income.py index f05caa2a49f..de6a23a93a7 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_countable_income.py +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_countable_income.py @@ -16,6 +16,8 @@ class nc_sa_countable_income(Variable): def formula(person, period, parameters): # SA is always a one-person budget: no spousal or parental deeming. + # SA-3210 IV prints the $20, $65 and one-half exclusions as SA's own + # rules; they match SSI's, so the SSI exclusion helper applies them. earned = max_( person("ssi_earned_income", period) - person("ssi_blind_or_disabled_working_student_exclusion", period), @@ -36,7 +38,13 @@ def formula(person, period, parameters): ) # Ongoing facility budgets assume the appropriate full federal rate # while SSA restores a prior institutional or household reduction. - # SSA couple computations remain an express exception to this rule. + # SSA couple computations remain an express exception to this rule + # (SA-3210 V.D.1.e). A one-third-reduced SSI may be budgeted for at + # most two months after admission (SA-3210 V.B.1.b); those first two + # months are not modeled, so every facility month is an ongoing month. + # The joint-claim test follows the marital unit: spouses who no longer + # live together belong in separate marital units (see + # nc_sa_care_setting), which also gives each the individual SSI amount. individual_claim = ~person("ssi_claim_is_joint", period.this_year) individual_fbr = parameters(period).gov.ssa.ssi.amount.individual federal = where( diff --git a/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_eligible.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_eligible.py new file mode 100644 index 00000000000..30db42526e9 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_eligible.py @@ -0,0 +1,84 @@ +from policyengine_us.model_api import * + + +class nc_sa_eligible(Variable): + value_type = bool + entity = Person + label = "North Carolina Special Assistance eligibility" + definition_period = MONTH + defined_for = StateCode.NC + reference = ( + "https://www.ncleg.gov/EnactedLegislation/Statutes/PDF/BySection/Chapter_108A/GS_108A-41.pdf#page=1", + # PDF pages 6-7, 40-41, 61, 190-191, 207, 209 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=6", + # PDF pages 14, 18, 41, 48 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=14", + # 10A NCAC 71P .0603 + "http://reports.oah.state.nc.us/ncac/title%2010a%20-%20health%20and%20human%20services/chapter%2071%20-%20adult%20and%20family%20support/subchapter%20p/subchapter%20p%20rules.pdf#page=11", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.nc.ncdhhs.sa + setting = person("nc_sa_care_setting", period) + settings = setting.possible_values + age = person("age", period.this_year) + in_home = (setting == settings.IN_HOME) | (setting == settings.IN_HOME_ENHANCED) + adult = age >= p.minimum_adult_age + age_eligible = adult | (person("is_blind", period.this_year) & ~in_home) + receives_ssi = (person("ssi", period) > 0) | person("receives_ssi", period) + # "To be eligible for SA, an individual must be eligible for SSI, or + # ineligible for SSI solely due to being over income for SSI" (SA-3100 + # I, II.A.6). An applicant whose income would qualify for SSI must apply + # (SA-3100 I; 10A NCAC 71P .0603(3)), and an SSI denial on other grounds + # such as resources also denies SA (SA-3110 V.K). Income here is SSI + # countable income, including deemed spousal income, so an applicant + # denied SSI for spousal income qualifies (SA-5400 V). + over_income_for_ssi = person("ssi_amount_if_eligible", period) <= person( + "ssi_countable_income", period + ) + # An SSI recipient automatically meets the categorical, financial and + # resource requirements (SA-3100 II.B; SA-3200 I.A). An applicant over + # income for SSI must meet SSI's other requirements "except for income" + # (SA-3100 II.C), but SA tests only the applicant's own resources: "The + # budget unit is always one" (SA-3200 I), so SSI's couple and spousal + # resource rules do not apply. SA-3200 I and SA-3210 IV print the $2,000 + # limit and the $20 and $65 exclusions as SA's own figures; they equal + # SSI's, so the federal parameters are used and an SSI reform carries + # over to SA. + aged_blind_disabled = person("is_ssi_aged_blind_disabled", period.this_year) + immigration_status = person("immigration_status", period.this_year) + citizen = immigration_status == immigration_status.possible_values.CITIZEN + immigration_eligible = citizen | person( + "is_ssi_qualified_noncitizen", period.this_year + ) + resource_limit = parameters( + period + ).gov.ssa.ssi.eligibility.resources.limit.individual + resources = person("ssi_countable_resources", period.this_year) + non_ssi_eligible = ( + over_income_for_ssi + & aged_blind_disabled + & immigration_eligible + & (resources <= resource_limit) + ) + # "If an individual's SA payment when rounded to the nearest lower + # whole dollar is $0.00, the individual is not eligible" (SA-3220 + # III.C). Read with III.A.4's half-up rounding, this yields the + # published cutoffs of $1,466.51 and $1,861.51 (II.E) and applies at a + # lower contracted rate (II.A). The monthly income test still applies + # when the first payment is prorated without subtracting income (IV.A, + # IV.D). + # NOTE: G.S. 108A-41(b)(2) caps countable income at the rate plus the + # personal needs allowance ("shall not exceed"), which would also admit + # countable income from $0.49 below that sum up to the sum itself, + # measured at the published rate. The two readings differ only in a + # prorated first month, where income is not deducted. The model follows + # the manual. + income_eligible = person("nc_sa_full_month_amount", period) >= 1 + return ( + (setting != settings.NONE) + & age_eligible + & (receives_ssi | non_ssi_eligible) + & income_eligible + & ~person("is_incarcerated", period) + ) diff --git a/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_facility_rate.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_facility_rate.py new file mode 100644 index 00000000000..6eca6cccbbc --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_facility_rate.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class nc_sa_facility_rate(Variable): + value_type = float + entity = Person + label = "North Carolina SA lower contracted monthly facility rate" + definition_period = MONTH + unit = USD + defined_for = StateCode.NC + default_value = 0 + documentation = "The monthly rate a facility charges a Special Assistance resident when it contracts for less than the published rate. Leave at 0 to use the published rate. In-home payments always use the published rate." + reference = ( + # PDF pages 204-206 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204", + ) diff --git a/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_full_month_amount.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_full_month_amount.py new file mode 100644 index 00000000000..cd99abcd671 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_full_month_amount.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * + + +class nc_sa_full_month_amount(Variable): + value_type = float + entity = Person + label = "North Carolina SA full-month amount" + definition_period = MONTH + unit = USD + defined_for = StateCode.NC + reference = ( + # PDF pages 207, 209 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=207", + # PDF pages 41-42 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=41", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.nc.ncdhhs.sa + maintenance = person("nc_sa_rate", period) + p.personal_needs_allowance + income = person("nc_sa_countable_income", period) + # The maintenance amount less TCMI, rounded to the nearest dollar with + # $0.50 rounding up (SA-3220 III.A). Work in whole cents so that + # annual-to-month arithmetic cannot move a value across the half dollar. + maintenance_cents = np.floor(maintenance.astype(np.float64) * 100 + 0.5) + income_cents = np.floor(income.astype(np.float64) * 100 + 0.5) + return max_(np.floor((maintenance_cents - income_cents) / 100 + 0.5), 0) diff --git a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_partial_month_days.py similarity index 51% rename from policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py rename to policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_partial_month_days.py index d8475bcacf0..dbd71e020cd 100644 --- a/policyengine_us/variables/gov/states/nc/dhhs/sa/nc_sa_days_eligible.py +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_partial_month_days.py @@ -1,19 +1,17 @@ from policyengine_us.model_api import * -class nc_sa_days_eligible(Variable): +class nc_sa_partial_month_days(Variable): value_type = int entity = Person - label = "North Carolina SA eligible days in initial month" + label = "North Carolina SA eligible days in a partial first month" definition_period = MONTH defined_for = StateCode.NC - documentation = "For an initial partial month, days from the date all eligibility conditions are satisfied through month end, including the first eligible day. Ongoing months default to the full month. This does not implement a discharge-month settlement." + default_value = 0 + documentation = "For a first month in which eligibility begins after the first day, the days from the first eligible day through the end of the month. Leave at 0 for a full month of eligibility. This does not implement a discharge-month settlement." reference = ( # PDF pages 209-210 "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=209", # PDF pages 42-43 "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=42", ) - - def formula(person, period, parameters): - return period.days diff --git a/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_rate.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_rate.py new file mode 100644 index 00000000000..8312c3b4471 --- /dev/null +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_rate.py @@ -0,0 +1,32 @@ +from policyengine_us.model_api import * + + +class nc_sa_rate(Variable): + value_type = float + entity = Person + label = "North Carolina SA monthly rate" + definition_period = MONTH + unit = USD + defined_for = StateCode.NC + reference = ( + # PDF pages 204-205 + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-Manual-Rev-June-2026.pdf#page=204", + "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=40", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.nc.ncdhhs.sa.rate + setting = person("nc_sa_care_setting", period) + settings = setting.possible_values + enhanced = (setting == settings.SPECIAL_CARE_UNIT) | ( + setting == settings.IN_HOME_ENHANCED + ) + published_rate = where(enhanced, p.enhanced, p.basic) + facility = (setting == settings.ADULT_CARE_HOME) | ( + setting == settings.SPECIAL_CARE_UNIT + ) + # A facility may charge an SA resident less than the published rate; + # the actual rate is then used in the budget (SA-3220 II.A). + contracted_rate = person("nc_sa_facility_rate", period) + lower_rate = facility & (contracted_rate > 0) + return where(lower_rate, min_(contracted_rate, published_rate), published_rate) From fae907a497a2c07cc1d951af8517150856a4f862 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 15 Oct 2026 18:38:36 -0400 Subject: [PATCH 5/6] Stop North Carolina SA partial-month days from carrying into later months A formula-less monthly input carries forward to later months, so a partial first month also prorated every month after it. A formula that returns 0 keeps a one-month entry to that month. Co-Authored-By: Claude Opus 5.5 --- .../gov/states/nc/ncdhhs/sa/nc_sa.yaml | 19 +++++++++++++++++++ .../nc/ncdhhs/sa/nc_sa_partial_month_days.py | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa.yaml index 0e0b726a437..d7a1f2a8e82 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa.yaml @@ -542,3 +542,22 @@ # 1,059 / 30 = 35.30; 35.30 * 25 + 70 = 952.50 -> 953. nc_sa: 2026-04: 953 + +- name: Case 40, partial-month days entered for one month leave the next month full. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: NC + age: 70 + nc_sa_care_setting: ADULT_CARE_HOME + nc_sa_partial_month_days: + 2026-01: 15 + output: + nc_sa_partial_month_days: + 2026-01: 15 + 2026-02: 0 + # January: 1,397 / 31 = 45.06; 45.06 * 15 + 70 = 745.90 -> 746. + # February is a full month: 1,397 + 70 - SSI 994 = 473. + nc_sa: + 2026-01: 746 + 2026-02: 473 diff --git a/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_partial_month_days.py b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_partial_month_days.py index dbd71e020cd..e4e6dce176b 100644 --- a/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_partial_month_days.py +++ b/policyengine_us/variables/gov/states/nc/ncdhhs/sa/nc_sa_partial_month_days.py @@ -15,3 +15,7 @@ class nc_sa_partial_month_days(Variable): # PDF pages 42-43 "https://policies.ncdhhs.gov/wp-content/uploads/Special-Assistance-In-Home-Program-Manual-Rev-June-2026.pdf#page=42", ) + + def formula(person, period, parameters): + # A formula stops a one-month input from carrying into later months. + return 0 From ecd199845543203094301394cb4332d74dcb09aa Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 16 Oct 2026 03:20:52 -0400 Subject: [PATCH 6/6] Cite the SSI couple resource limit in North Carolina SA Case 9 comment Co-Authored-By: Claude Opus 5.5 --- .../baseline/gov/states/nc/ncdhhs/sa/nc_sa_eligible.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_eligible.yaml index 4bd86188e2a..cfcbdb4e257 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/sa/nc_sa_eligible.yaml @@ -182,7 +182,8 @@ state_code: NC output: # No income, so SSI would be payable, but 1,800 + 3,500 = 5,300 exceeds - # the 2,000 limit. + # the 3,000 limit for an individual living with an ineligible spouse + # (20 CFR 416.1205(a)). ssi: [0, 0] # person1's own resources (1,800) would pass SA-3200 I, but SA requires # SSI eligibility when the income would qualify.