diff --git a/changelog.d/vt-ssp-2026.added.md b/changelog.d/vt-ssp-2026.added.md new file mode 100644 index 00000000000..20d0d98ce03 --- /dev/null +++ b/changelog.d/vt-ssp-2026.added.md @@ -0,0 +1 @@ +Add Vermont federally administered optional AABD supplement rates and calculations for 2026. Add Vermont 2026 Essential Person grants alongside federally administered AABD. diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index d2cbd6ba2d4..6bc8717d523 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -71,6 +71,8 @@ values: - sc_ssi_state_supplement # Texas benefits - tx_ssi_state_supplement + # Vermont benefits + - vt_aabd # Virginia benefits - va_child_care_subsidies # Massachusetts benefits @@ -163,6 +165,8 @@ values: - sc_ssi_state_supplement # Texas benefits - tx_ssi_state_supplement + # Vermont benefits + - vt_aabd # Virginia benefits - va_child_care_subsidies # West Virginia benefits @@ -265,6 +269,8 @@ values: - sc_ssi_state_supplement # Texas benefits - tx_ssi_state_supplement + # Vermont benefits + - vt_aabd # Virginia benefits - va_child_care_subsidies # West Virginia benefits @@ -368,6 +374,8 @@ values: - sc_ssi_state_supplement # Texas benefits - tx_ssi_state_supplement + # Vermont benefits + - vt_aabd # Virginia benefits - va_child_care_subsidies # West Virginia benefits @@ -401,6 +409,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 0df11213631..25c5eb05b55 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 + - vt_aabd - general_assistance - total_unemployment_compensation - child_support_received @@ -29,6 +30,7 @@ values: - disability_benefits - survivor_benefits - financial_assistance + metadata: unit: list period: year diff --git a/policyengine_us/parameters/gov/ssa/ssi/state_supplement/minimum_payment.yaml b/policyengine_us/parameters/gov/ssa/ssi/state_supplement/minimum_payment.yaml new file mode 100644 index 00000000000..1d3e6e2e6b6 --- /dev/null +++ b/policyengine_us/parameters/gov/ssa/ssi/state_supplement/minimum_payment.yaml @@ -0,0 +1,13 @@ +description: The Social Security Administration pays at least this monthly amount to each recipient of a positive federally administered optional state supplement. +values: + 2026-01-01: 1 + +metadata: + unit: currency-USD + period: month + label: SSI optional state supplement minimum payment + reference: + - title: 20 CFR 416.2020(c) - Minimum federally administered supplementary payment + href: https://www.ssa.gov/OP_Home/cfr20/416/416-2020.htm + - title: POMS SI 02001.005 A.2 and B.2 - Minimum benefit for individuals and members of couples + href: https://secure.ssa.gov/poms.nsf/lnx/0502001005 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/aabd/amount/couple.yaml b/policyengine_us/parameters/gov/states/vt/dcf/aabd/amount/couple.yaml new file mode 100644 index 00000000000..2615a7a89f6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/aabd/amount/couple.yaml @@ -0,0 +1,32 @@ +description: Vermont provides this amount as the optional state supplement for an eligible couple under the Aid to the Aged, Blind or Disabled program. + +metadata: + unit: currency-USD + period: month + label: Vermont AABD couple payment rate + breakdown: + - vt_aabd_living_arrangement + reference: + - title: POMS SI 01415.058 M.2.b, Vermont January 2026 couple payment levels + href: https://secure.ssa.gov/poms.nsf/lnx/0501415058#m2b + +INDEPENDENT: + 2026-01-01: 105.8 + +HOUSEHOLD_OF_ANOTHER: + 2026-01-01: 51.69 + +RESIDENTIAL_CARE_LEVEL_III: + 2026-01-01: 103.54 + +RESIDENTIAL_CARE_LEVEL_IV: + 2026-01-01: 601.4 + +CUSTODIAL_CARE: + 2026-01-01: 356.12 + +MEDICAID_INSTITUTION: + 2026-01-01: 99.85 + +NONE: + 2026-01-01: 0 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/aabd/amount/individual.yaml b/policyengine_us/parameters/gov/states/vt/dcf/aabd/amount/individual.yaml new file mode 100644 index 00000000000..7db6c2a8d35 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/aabd/amount/individual.yaml @@ -0,0 +1,32 @@ +description: Vermont provides this amount as the optional state supplement for an eligible individual under the Aid to the Aged, Blind or Disabled program. + +metadata: + unit: currency-USD + period: month + label: Vermont AABD individual payment rate + breakdown: + - vt_aabd_living_arrangement + reference: + - title: POMS SI 01415.058 M.2.a, Vermont January 2026 recipient payment levels + href: https://secure.ssa.gov/poms.nsf/lnx/0501415058#m2a + +INDEPENDENT: + 2026-01-01: 55.68 + +HOUSEHOLD_OF_ANOTHER: + 2026-01-01: 42.05 + +RESIDENTIAL_CARE_LEVEL_III: + 2026-01-01: 51.77 + +RESIDENTIAL_CARE_LEVEL_IV: + 2026-01-01: 239.62 + +CUSTODIAL_CARE: + 2026-01-01: 105.6 + +MEDICAID_INSTITUTION: + 2026-01-01: 49.93 + +NONE: + 2026-01-01: 0 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/couple_increment.yaml b/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/couple_increment.yaml new file mode 100644 index 00000000000..8233aa93215 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/couple_increment.yaml @@ -0,0 +1,13 @@ +description: Vermont provides this amount as the Essential Person increment for an eligible couple under the Aid to the Aged, Blind or Disabled program. +values: + 2025-01-01: 183.84 + +metadata: + unit: currency-USD + period: month + label: Vermont Essential Person couple payment increment + reference: + - title: "AABD rule 2784(C) #page 44-45" + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=44 + - title: Bulletin 25-26, P2740B, AABD-EP payment maximums + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/B25-26.pdf#page=12 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/employment_expense.yaml b/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/employment_expense.yaml new file mode 100644 index 00000000000..ac961f3ad30 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/employment_expense.yaml @@ -0,0 +1,11 @@ +description: Vermont deducts this amount from each worker's earnings as an Essential Person employment expense under the Aid to the Aged, Blind or Disabled program. +values: + 2026-01-01: 90 + +metadata: + unit: currency-USD + period: month + label: Vermont Essential Person employment expense deduction per worker + reference: + - title: AABD rules 2786 interpretive memo + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=50 diff --git a/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/rate.yaml b/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/rate.yaml new file mode 100644 index 00000000000..00de7ce7524 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/dcf/aabd/ep/rate.yaml @@ -0,0 +1,23 @@ +description: Vermont provides this share of the Essential Person maximum above the combined payment under the Aid to the Aged, Blind or Disabled program. + +metadata: + unit: /1 + period: year + label: Vermont Essential Person care fraction + breakdown: + - vt_aabd_ep_level + reference: + - title: AABD rules 2784(A) + href: https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=44 + +NONE: + 2026-01-01: 0 + +FULL: + 2026-01-01: 1 + +PARTIAL_67: + 2026-01-01: 0.67 + +PARTIAL_34: + 2026-01-01: 0.34 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 e9baee603e8..1a50d741881 100644 --- a/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/usda/snap/income/sources/unearned.yaml @@ -49,6 +49,7 @@ values: - nm_ssi_state_supplement - sc_ssi_state_supplement - tx_ssi_state_supplement + - vt_aabd - wa_ssp # Listed elsewhere: ca_state_supplement is an SPM unit variable with no # person-level counterpart. policyengine-core projects a group value onto @@ -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) + 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 5250ab3aa89..986172d9ecc 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1093,6 +1093,17 @@ 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: VT + status: partial + name: Vermont optional AABD + full_name: Vermont Aid to the Aged, Blind or Disabled optional supplement + variable: vt_aabd + parameter_prefix: gov.states.vt.dcf.aabd + verified_years: "2026" + notes: >- + Models 2026 federally administered supplements and state Essential Person + grants. Mandatory grandfathered payments and the state-administered + limited-nursing Level III supplement are not modeled. - id: social_security name: Social Security diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/integration.yaml new file mode 100644 index 00000000000..3821c74ea92 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/integration.yaml @@ -0,0 +1,299 @@ +- name: Case 1, independent couple. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + person2: + age: 65 + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + # Joint couple, no countable income: 105.80 couple rate / 2 = 52.90 each. + vt_aabd: [52.9, 52.9] + +- name: Case 2, residential_care_level_iii couple. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + vt_aabd_care_setting: RESIDENTIAL_CARE_LEVEL_III + person2: + age: 65 + vt_aabd_care_setting: RESIDENTIAL_CARE_LEVEL_III + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + # 103.54 couple rate / 2 = 51.77 each. + vt_aabd: [51.77, 51.77] + +- name: Case 3, residential_care_level_iv couple. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + vt_aabd_care_setting: RESIDENTIAL_CARE_LEVEL_IV + person2: + age: 65 + vt_aabd_care_setting: RESIDENTIAL_CARE_LEVEL_IV + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + # 601.40 couple rate / 2 = 300.70 each. + vt_aabd: [300.7, 300.7] + +- name: Case 4, custodial_care couple. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + vt_aabd_care_setting: CUSTODIAL_CARE + person2: + age: 65 + vt_aabd_care_setting: CUSTODIAL_CARE + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + # 356.12 couple rate / 2 = 178.06 each. + vt_aabd: [178.06, 178.06] + +- name: Case 5, household_of_another couple. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + person2: + age: 65 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + # 51.69 couple rate / 2 = 25.845, rounded up to the cent = 25.85 each. + vt_aabd: [25.85, 25.85] + +- name: Case 6, medicaid_institution couple. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + person2: + age: 65 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + # Both in the facility, so the couple computation applies: + # 99.85 couple rate / 2 = 49.925, rounded up to the cent = 49.93 each. + vt_aabd: [49.93, 49.93] + +- name: Case 7, annual benefits and SNAP income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + # SNAP take-up off so spm_unit_benefits does not rest on projected SNAP amounts. + takes_up_snap_if_eligible: false + output: + # 55.68 independent rate * 12 = 668.16. + vt_aabd: 668.16 + household_state_benefits: 668.16 + # SSI 994 * 12 = 11,928 + 668.16 = 12,596.16. + snap_unearned_income: 12_596.16 + # SSI 11,928 + vt_aabd 668.16; SNAP is 0 without take-up. + spm_unit_benefits: 12_596.16 + +- name: Case 8, Essential-person grant enters the annual Vermont benefit total. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + vt_aabd_ep_level: + 2026: FULL + output: + # 994 * 12 = 11,928. + ssi: 11_928 + # 55.68 * 12 = 668.16. + vt_aabd_federal: 668.16 + # Monthly maximum 1,491 + 105.80 = 1,596.80; countable 994 + 55.68 = 1,049.68; + # floor(1,596.80 - 1,049.68) = floor(547.12) = 547 * 12 = 6,564. + vt_aabd_ep: 6_564 + # 668.16 + 6,564 = 7,232.16. + vt_aabd: 7_232.16 + # 11,928 + 7,232.16 = 19,160.16. + snap_unearned_income: 19_160.16 + # SSI plus the whole AABD payment, including the Essential Person grant: + # 11,928 + 7,232.16 = 19,160.16. + hud_unearned_income: 19_160.16 + +- name: Case 9, Two eligible spouses share one essential-person grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: FULL + person2: + age: 70 + vt_aabd_ep_level: FULL + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + # Couple maximum 1,491 + 105.80 + 183.84 = 1,780.64; countable SSI 745.50 * 2 + # + AABD 52.90 * 2 = 1,596.80; floor(183.84) = 183 / 2 = 91.50 each. + vt_aabd_ep: [91.5, 91.5] + # 52.90 AABD + 91.50 Essential Person grant = 144.40 each. + vt_aabd: [144.4, 144.4] + +- name: Case 10, Ineligible spouse earnings do not get the ABD half-earnings deduction. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: FULL + person2: + age: 40 + employment_income: 6_000 + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + # person2 (40, not blind or disabled) is the ineligible spouse. + vt_aabd_ep_is_spouse: [true, false] + # SSI receipt passes the resource test for the group. + vt_aabd_ep_resources_eligible: [true, true] + # Spouse earnings 500 - 90 employment expense - 20 general = 390, with no + # half-earnings exclusion, + SSI 994 + AABD 55.68 = 1,439.68. + vt_aabd_ep_countable_income: [1_439.68, 1_439.68] + # floor(1,596.80 - 1,439.68) = floor(157.12) = 157 for the recipient only. + vt_aabd_ep: [157, 0] + +- name: Case 11, Vermont essential care for a disabled member counts as HUD income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 40 + meets_ssi_disability_criteria: true + is_disabled: true + vt_aabd_ep_level: FULL + output: + # AABD 55.68 * 12 = 668.16 + Essential Person grant floor(1,596.80 - 1,049.68) + # = 547 * 12 = 6,564; 668.16 + 6,564 = 7,232.16. + vt_aabd: 7_232.16 + # SSI 994 * 12 = 11,928 + 7,232.16 = 19,160.16. + snap_unearned_income: 19_160.16 + # HUD counts the whole AABD payment, including the Essential Person grant: + # 11,928 + 7,232.16 = 19,160.16. + hud_unearned_income: 19_160.16 + +- name: Case 12, One spouse in a Medicaid facility is paid as an individual. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + person2: + age: 70 + marital_units: + couple: + members: [person1, person2] + tax_units: + unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + vt_aabd_living_arrangement: [MEDICAID_INSTITUTION, INDEPENDENT] + # 20 CFR 416.414(b)(3): each spouse is paid at an individual rate. + ssi_couple_computation_applies: [false, false] + # $30 facility rate and the 994 individual FBR. + ssi_amount_if_eligible: [30, 994] + ssi: [30, 994] + # POMS SI 01415.058 M.2.a individual rates: code I 49.93, code A 55.68. + vt_aabd: [49.93, 55.68] diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/vt_aabd.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/vt_aabd.yaml new file mode 100644 index 00000000000..9e216d7a7e0 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/vt_aabd.yaml @@ -0,0 +1,196 @@ +# POMS SI 01415.058 M, Vermont January 2026 optional supplement schedule. +- name: Case 1, independent individual. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + output: + vt_aabd_living_arrangement: INDEPENDENT + vt_aabd: 55.68 + +- name: Case 2, residential care level iii individual. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + vt_aabd_care_setting: RESIDENTIAL_CARE_LEVEL_III + output: + vt_aabd: 51.77 + +- name: Case 3, residential care level iv individual. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + vt_aabd_care_setting: RESIDENTIAL_CARE_LEVEL_IV + output: + vt_aabd: 239.62 + +- name: Case 4, custodial care individual. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + vt_aabd_care_setting: CUSTODIAL_CARE + output: + vt_aabd: 105.6 + +- name: Case 5, household of another individual. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + output: + # Federal another person's household maps to HOUSEHOLD_OF_ANOTHER. + vt_aabd_living_arrangement: HOUSEHOLD_OF_ANOTHER + # No countable income: 42.05 rate - 0 excess over the 662.67 reduced FBR = 42.05. + vt_aabd: 42.05 + +- name: Case 6, medicaid institution individual. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + output: + # Federal Medicaid-paid medical treatment facility maps to MEDICAID_INSTITUTION. + vt_aabd_living_arrangement: MEDICAID_INSTITUTION + # No countable income: 49.93 rate - 0 excess over the $30 facility FBR = 49.93. + vt_aabd: 49.93 + +- name: Case 7, a recipient who declines SSI gets no supplement. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + takes_up_ssi_if_eligible: false + output: + # 33 V.S.A. § 1301(3) requires SSI receipt if eligible. + ssi: 0 + vt_aabd: 0 + +- name: Case 8, Chittenden County independent living uses the same rate as the rest of the state. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + county: CHITTENDEN_COUNTY_VT + age: 65 + output: + # POMS SI 01415.058 M.2.a: OS code B (Chittenden County) and code A both + # pay 55.68. + vt_aabd_living_arrangement: INDEPENDENT + vt_aabd: 55.68 + +- name: Case 9, income above federal SSI standard. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + social_security_retirement: 12_360 + output: + vt_aabd: 39.68 + +- name: Case 10, income at combined cutoff. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + social_security_retirement: 12_836.16 + output: + vt_aabd: 0 + +- name: Case 11, minimum positive payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + social_security_retirement: 12_836.04 + output: + vt_aabd: 1 + +- name: Case 12, excess resources. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + ssi_countable_resources: 2_001 + output: + vt_aabd: 0 + +- name: Case 13, not aged blind or disabled. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 30 + output: + vt_aabd: 0 + +- name: Case 14, non Vermont resident. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NH + age: 65 + output: + vt_aabd: 0 + +- name: Case 15, a care setting for one of two unrelated people leaves the other independent. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_care_setting: CUSTODIAL_CARE + person2: + age: 70 + marital_units: + marital_unit1: + members: [person1] + marital_unit2: + members: [person2] + tax_units: + tax_unit1: + members: [person1] + tax_unit2: + members: [person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + vt_aabd_living_arrangement: [CUSTODIAL_CARE, INDEPENDENT] + # Individual rates with no income: 105.60 custodial care and 55.68 independent. + vt_aabd: [105.6, 55.68] + +- name: Case 16, a Title XIX facility precedes a care setting. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 65 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + vt_aabd_care_setting: RESIDENTIAL_CARE_LEVEL_III + output: + # POMS SI 01415.058 M.2.a uses code I with federal arrangement D. + vt_aabd_living_arrangement: MEDICAID_INSTITUTION + # 49.93 rate - 0 excess over the $30 facility FBR = 49.93. + vt_aabd: 49.93 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/vt_aabd_ep.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/vt_aabd_ep.yaml new file mode 100644 index 00000000000..5c26b2057e4 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dcf/aabd/vt_aabd_ep.yaml @@ -0,0 +1,500 @@ +- name: Case 1, Full essential-person payment supplements existing SSI and AABD. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + ssi: 994 + vt_aabd_federal: 55.68 + vt_aabd_ep_level: FULL + output: + vt_aabd_ep: 547 + +- name: Case 2, Sixty-seven percent care level. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + ssi: 994 + vt_aabd_federal: 55.68 + vt_aabd_ep_level: PARTIAL_67 + output: + vt_aabd_ep: 366 + +- name: Case 3, Thirty-four percent care level. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + ssi: 994 + vt_aabd_federal: 55.68 + vt_aabd_ep_level: PARTIAL_34 + output: + vt_aabd_ep: 186 + +- name: Case 4, No certified essential-person arrangement. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + ssi: 994 + vt_aabd_federal: 55.68 + vt_aabd_ep_level: NONE + output: + vt_aabd_ep: 0 + +- name: Case 5, An ineligible spouse cannot qualify at the 34 percent service level. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: PARTIAL_34 + person2: + age: 60 + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + # person2 (60, not blind or disabled) is the ineligible spouse. + vt_aabd_ep_is_spouse: [true, false] + # No spouse income, so no deeming. + ssi: [994, 0] + vt_aabd_federal: [55.68, 0] + # Rule 2784(A)(3) pays the 34 percent level only for a nonspouse. + vt_aabd_ep: [0, 0] + +- name: Case 6, Prorated nonspouse essential-person net income counts. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + ssi: 994 + vt_aabd_federal: 55.68 + vt_aabd_ep_level: FULL + vt_aabd_ep_additional_countable_income: 100 + output: + vt_aabd_ep: 447 + +- name: Case 7, Income at payment maximum. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + ssi: 994 + vt_aabd_federal: 55.68 + vt_aabd_ep_level: FULL + vt_aabd_ep_additional_countable_income: 547.12 + output: + # SSI 994 + AABD 55.68 + 547.12 additional = 1,596.80, the payment maximum. + vt_aabd_ep_countable_income: 1_596.8 + vt_aabd_ep: 0 + +- name: Case 8, Additional countable income for one of two unrelated recipients stays with that recipient. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: FULL + vt_aabd_ep_additional_countable_income: 100 + person2: + age: 70 + vt_aabd_ep_level: FULL + marital_units: + marital_unit1: + members: [person1] + marital_unit2: + members: [person2] + tax_units: + tax_unit1: + members: [person1] + tax_unit2: + members: [person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + # SSI 994 + AABD 55.68 = 1,049.68, plus 100 additional for person1 only. + vt_aabd_ep_countable_income: [1_149.68, 1_049.68] + # floor(1,596.80 - 1,149.68) = 447; floor(1,596.80 - 1,049.68) = 547. + vt_aabd_ep: [447, 547] + +- name: Case 9, A spouse who is not aged, blind or disabled defaults as the essential person in another household. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: FULL + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + person2: + age: 60 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + # person1 is aged and person2 (60, not blind or disabled) is in the same marital unit. + vt_aabd_ep_is_spouse: [true, false] + vt_aabd_living_arrangement: [HOUSEHOLD_OF_ANOTHER, HOUSEHOLD_OF_ANOTHER] + # No spouse income, so no deeming: 994 * (1 - 1/3) = 662.67 reduced FBR. + ssi: [662.67, 0] + # 42.05 another-household rate - 0 excess = 42.05. + vt_aabd_federal: [42.05, 0] + # Maximum 994 + 55.68 = 1,049.68; countable 662.67 + 42.05 = 704.72; + # floor(1,049.68 - 704.72) = floor(344.96) = 344. + vt_aabd_ep: [344, 0] + +- name: Case 10, Essential-person coverage does not extend to residential care. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + vt_aabd_ep_level: FULL + vt_aabd_care_setting: RESIDENTIAL_CARE_LEVEL_III + output: + vt_aabd_living_arrangement: RESIDENTIAL_CARE_LEVEL_III + vt_aabd_federal: 51.77 + vt_aabd_ep: 0 + +- name: Case 11, Nonspouse essential person cannot qualify in another household. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + vt_aabd_ep_level: FULL + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + output: + vt_aabd_living_arrangement: HOUSEHOLD_OF_ANOTHER + # 994 * (1 - 1/3) = 662.67 reduced FBR. + ssi: 662.67 + vt_aabd_federal: 42.05 + # Rule 2783.2 excludes a nonspouse essential person in another's household. + vt_aabd_ep: 0 + +- name: Case 12, Ineligible spouse qualifies at 67 percent in another household. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: PARTIAL_67 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + person2: + age: 50 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + vt_aabd_ep_is_spouse: [true, false] + ssi: [662.67, 0] + vt_aabd_federal: [42.05, 0] + # P2740B 67 percent another's-household maximum 935.84; + # floor(935.84 - (662.67 + 42.05)) = floor(231.12) = 231. + vt_aabd_ep: [231, 0] + +- name: Case 13, Non-SSI recipient requires verified resource eligibility. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + vt_aabd_ep_level: FULL + # Scaled low earner claiming at 65: $15,238 in 2025 dollars (2025 Trustees + # Report Table V.C7) * 1.028 COLA = about $15,665 a year, $1,305.42 a month. + social_security_retirement: 15_665 + output: + # SSI countable 1,305.42 - 20 = 1,285.42 > 994 FBR, so SSI is 0. + ssi: 0 + # 55.68 - (1,285.42 - 994) excess of 291.42 < 0, so no AABD supplement. + vt_aabd_federal: 0 + # No SSI, AABD or reported SSI receipt and no state resource determination. + vt_aabd_ep_resources_eligible: false + vt_aabd_ep: 0 + +- name: Case 14, State-only essential-person grant with verified resources. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + vt_aabd_ep_level: FULL + vt_aabd_ep_passes_state_resource_test: true + # Same scaled low earner Social Security benefit as Case 13: $1,305.42 a month. + social_security_retirement: 15_665 + output: + # SSI countable 1,285.42 > 994 FBR and 291.42 excess > 55.68 supplement. + ssi: 0 + vt_aabd_federal: 0 + vt_aabd_ep_resources_eligible: true + # 1,305.42 - 20 general exclusion = 1,285.42. + vt_aabd_ep_countable_income: 1_285.42 + # Maximum 1,491 + 105.80 = 1,596.80; floor(1,596.80 - 1,285.42) = floor(311.38) = 311. + vt_aabd_ep: 311 + +- name: Case 15, A worker receives the state employment-expense deduction. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + vt_aabd_ep_level: FULL + vt_aabd_ep_passes_state_resource_test: true + # Full time at Vermont's 2026 minimum wage: $14.42 * 2,080 hours = $29,993.60, + # $2,499.47 a month. + employment_income: 29_993.6 + output: + # SSI countable (2,499.47 - 20 - 65) * 0.5 = 1,207.23 > 994 FBR. + ssi: 0 + # 1,207.23 - 994 = 213.23 excess > 55.68 supplement. + vt_aabd_federal: 0 + # (2,499.47 - 90 employment expense - 20 - 65) * 0.5 = 1,162.23. + vt_aabd_ep_countable_income: 1_162.23 + # floor(1,596.80 - 1,162.23) = floor(434.57) = 434. + vt_aabd_ep: 434 + +- name: Case 16, Needs-based veterans pension does not get the general exclusion. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: VT + age: 70 + vt_aabd_ep_level: FULL + vt_aabd_ep_passes_state_resource_test: true + # VA pension MAPR for a veteran with no dependents from December 1, 2025: + # $17,441 a year, $1,453.42 a month, with no other income. + veterans_benefits: 17_441 + output: + # SSI countable 1,453.42 - 20 = 1,433.42 > 994 FBR. + ssi: 0 + # 1,433.42 - 994 = 439.42 excess > 55.68 supplement. + vt_aabd_federal: 0 + # The needs-based pension counts in full, without the $20 general exclusion. + vt_aabd_ep_countable_income: 1_453.42 + # floor(1,596.80 - 1,453.42) = floor(143.38) = 143. + vt_aabd_ep: 143 + +- name: Case 17, Out of state. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: NH + age: 70 + ssi: 994 + vt_aabd_federal: 55.68 + vt_aabd_ep_level: FULL + output: + vt_aabd_ep: 0 + +- name: Case 18, A nonspouse essential person qualifies at 34 percent next to an ineligible spouse. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: PARTIAL_34 + vt_aabd_ep_nonspouse_essential_person: true + person2: + age: 45 + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + # The certified essential person is not the spouse (rules 2781(E), 2782). + vt_aabd_ep_is_spouse: [false, false] + # SSI 994 + AABD 55.68; the spouse has no income. + vt_aabd_ep_countable_income: [1_049.68, 1_049.68] + # 1,049.68 + 0.34 * (1,596.80 - 1,049.68) = 1,235.70; + # floor(1,235.70 - 1,049.68) = floor(186.02) = 186. + vt_aabd_ep: [186, 0] + # 55.68 AABD + 186 Essential Person grant = 241.68. + vt_aabd: [241.68, 0] + +- name: Case 19, A state resource determination for one of two unrelated recipients stays with that recipient. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: FULL + vt_aabd_ep_passes_state_resource_test: true + # Same scaled low earner Social Security benefit as Case 13: $1,305.42 a month. + social_security_retirement: 15_665 + person2: + age: 70 + vt_aabd_ep_level: FULL + marital_units: + marital_unit1: + members: [person1] + marital_unit2: + members: [person2] + tax_units: + tax_unit1: + members: [person1] + tax_unit2: + members: [person2] + households: + household: + members: [person1, person2] + state_code: VT + output: + # person1 passes the state test; person2 receives SSI. + vt_aabd_ep_resources_eligible: [true, true] + # floor(1,596.80 - 1,285.42) = 311; floor(1,596.80 - 1,049.68) = 547. + vt_aabd_ep: [311, 547] + # person2: 55.68 AABD + 547 = 602.68. + vt_aabd: [311, 602.68] + +- name: Case 20, A nonspouse flag on one recipient leaves another recipient's spouse as the essential person. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_nonspouse_essential_person: true + person2: + age: 70 + vt_aabd_ep_level: FULL + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + person3: + age: 60 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + marital_units: + marital_unit1: + members: [person1] + marital_unit2: + members: [person2, person3] + tax_units: + tax_unit1: + members: [person1] + tax_unit2: + members: [person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: VT + output: + vt_aabd_ep_is_spouse: [false, true, false] + # person2: floor(1,049.68 - (662.67 + 42.05)) = floor(344.96) = 344. + vt_aabd_ep: [0, 344, 0] + # person1: 55.68 independent rate; person2: 42.05 + 344 = 386.05. + vt_aabd: [55.68, 386.05, 0] + +- name: Case 21, One certified essential person for an eligible couple uses the couple maximum. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: FULL + person2: + age: 70 + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + # Rules 2780(A), 2783.1 and 2784(C): one grant for the couple. + # Couple maximum 1,491 + 105.80 + 183.84 = 1,780.64; countable SSI 745.50 * 2 + # + AABD 52.90 * 2 = 1,596.80; floor(183.84) = 183 / 2 = 91.50 each. + vt_aabd_ep: [91.5, 91.5] + +- name: Case 22, An eligible couple certified at different levels uses the higher 67 percent couple level. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + vt_aabd_ep_level: PARTIAL_67 + person2: + age: 70 + vt_aabd_ep_level: PARTIAL_34 + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + # P2740B 67 percent couple maximum 1,719.97; countable SSI 745.50 * 2 + # + AABD 52.90 * 2 = 1,596.80; floor(123.17) = 123 / 2 = 61.50 each. + vt_aabd_ep: [61.5, 61.5] + +- name: Case 23, A spouse in a Medicaid facility is left out of the at-home spouse's group income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + social_security_retirement: 15_665 + person2: + age: 70 + vt_aabd_ep_level: FULL + marital_units: + couple: + members: [person1, person2] + households: + home: + members: [person1, person2] + state_code: VT + output: + # Rule 2782: only household members count; the at-home spouse has SSI + # 994 + AABD 55.68 = 1,049.68 against the 1,596.80 individual maximum. + vt_aabd_ep_countable_income: [1_049.68, 1_049.68] + vt_aabd_ep: [0, 547] diff --git a/policyengine_us/variables/gov/ssa/ssi/_ssi_state_supplement.py b/policyengine_us/variables/gov/ssa/ssi/_ssi_state_supplement.py new file mode 100644 index 00000000000..5e0c84da3f3 --- /dev/null +++ b/policyengine_us/variables/gov/ssa/ssi/_ssi_state_supplement.py @@ -0,0 +1,53 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.ssa.ssi.eligibility.status.ssi_federal_living_arrangement import ( + SSIFederalLivingArrangement, +) + + +def ssi_state_supplement( + person, period, parameters, individual_rate, couple_rate, federal_payment_rules=True +): + """Apply SSI-based supplement income, couple and spouse-deeming rules. + + Rates are state supplements, excluding federal SSI. Each member supplies + the same-category couple rate; averaging these rates implements mixed + arrangements where the state's published schedule supports that method. + Set federal_payment_rules only for SSA-administered payments; state + administrators do not inherit the federal rounding or minimum payment. + Sources: 20 CFR 416.2025; POMS SI 01320.430 D, SI 02005.030 G, + SI 02001.005 and SI 02001.010. Callers apply state-specific eligibility. + """ + joint = person("ssi_couple_computation_applies", period) + couple_total = person.marital_unit.sum(couple_rate / 2) + standard = where(joint, couple_total / 2, individual_rate) + countable = person("ssi_countable_income", period) + federal_amount = person("ssi_amount_if_eligible", period) + excess = max_(countable - federal_amount, 0) + payment = max_(standard - excess, 0) + + deeming = person("is_ssi_spousal_deeming_applies", period.this_year) + deemed = person("ssi_income_deemed_from_ineligible_spouse", period) + own_income = max_(countable - deemed, 0) + ssi = parameters(period).gov.ssa.ssi + federal_la = person("ssi_federal_living_arrangement", period) + own_federal_amount = ssi.amount.individual * where( + federal_la == SSIFederalLivingArrangement.ANOTHER_PERSONS_HOUSEHOLD, + 1 - ssi.amount.one_third_reduction_rate, + 1, + ) + own_payment = max_(individual_rate - max_(own_income - own_federal_amount, 0), 0) + # SI 01320.430 D.2: use the higher individual rate where it exceeds + # the couple rate (including New Jersey's ineligible-spouse variation). + deemed_payment = max_(max_(couple_rate, individual_rate) - excess, 0) + payment = where(deeming, min_(own_payment, deemed_payment), payment) + + eligible = person("is_ssi_eligible", period) & person( + "takes_up_ssi_if_eligible", period.this_year + ) + # Federal administration discards digits beyond the third decimal place, + # rounds up to cents, and pays at least one dollar for a positive amount. + rounded = np.ceil(np.floor(payment * 1_000) / 10) / 100 + federal_payment = where( + rounded > 0, max_(rounded, ssi.state_supplement.minimum_payment), 0 + ) + return eligible * where(federal_payment_rules, federal_payment, payment) diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd.py new file mode 100644 index 00000000000..0e0494c54cd --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class vt_aabd(Variable): + value_type = float + entity = Person + label = "Vermont Aid to the Aged, Blind or Disabled supplements" + definition_period = MONTH + unit = USD + defined_for = StateCode.VT + reference = ( + # 33 V.S.A. §§ 1301(3), 1309(a) + "https://legislature.vermont.gov/statutes/fullchapter/33/013", + "https://secure.ssa.gov/poms.nsf/lnx/0501415058#m2", + # PDF pages 27, 36 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=27", + ) + adds = ["vt_aabd_federal", "vt_aabd_ep"] diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_care_setting.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_care_setting.py new file mode 100644 index 00000000000..5b68c7f78d9 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_care_setting.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * + + +class VTAABDCareSetting(Enum): + RESIDENTIAL_CARE_LEVEL_III = ( + "Level III residential care home with assistive community care (code C)" + ) + RESIDENTIAL_CARE_LEVEL_IV = ( + "Level IV residential care home or therapeutic community residence (code G)" + ) + CUSTODIAL_CARE = "Custodial care family home (code H)" + NONE = "No state-identified care setting" + + +class vt_aabd_care_setting(Variable): + value_type = Enum + entity = Person + label = "Vermont AABD care setting" + definition_period = MONTH + defined_for = StateCode.VT + possible_values = VTAABDCareSetting + default_value = VTAABDCareSetting.NONE + reference = ( + "https://secure.ssa.gov/poms.nsf/lnx/0501415058#m1", + # PDF pages 31-33 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=31", + ) + documentation = "Residence in a care setting that meets the state's licensing, certification and service rules (rules 2764.1, 2764.3 and 2766)." diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep.py new file mode 100644 index 00000000000..b22032e9017 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep.py @@ -0,0 +1,69 @@ +from policyengine_us.model_api import * + + +class vt_aabd_ep(Variable): + value_type = float + entity = Person + label = "Vermont state-administered Essential Person grant" + definition_period = MONTH + unit = USD + # PDF pages 36, 43-45, 49-50 + reference = "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=36" + defined_for = StateCode.VT + + def formula(person, period, parameters): + p = parameters(period).gov.states.vt.dcf.aabd + federal = parameters(period).gov.ssa.ssi.amount + level = person("vt_aabd_ep_level", period) + levels = level.possible_values + arrangement = person("vt_aabd_living_arrangement", period) + la = arrangement.possible_values + independent = arrangement == la.INDEPENDENT + other = arrangement == la.HOUSEHOLD_OF_ANOTHER + spouse = person("vt_aabd_ep_is_spouse", period) + valid_level = (level != levels.NONE) & ~(spouse & (level == levels.PARTIAL_34)) + # vt_aabd_ep_resources_eligible already tests the whole marital unit. + abd_eligible = ( + person("is_ssi_aged_blind_disabled", period.this_year) + & (independent | (other & spouse)) + & person("vt_aabd_ep_resources_eligible", period) + & ~person("is_incarcerated", period) + & person("takes_up_ssi_if_eligible", period.this_year) + ) + # Rules 2780(A), 2783.1 and 2784(C): an eligible couple receives one + # grant for a shared essential person. The rules do not address + # different levels for each spouse; use the highest level certified for + # either spouse. + certified = abd_eligible & valid_level + person_eligible = abd_eligible & person.marital_unit.any(certified) + rate = person.marital_unit.max(where(certified, p.ep.rate[level], 0)) + count = person.marital_unit.sum(person_eligible) + couple = count > 1 + # Rule 2784(B)-(C): the individual maximum is the combined couple + # payment, the another-household maximum is the combined independent + # individual payment, and the couple maximum adds an increment to the + # combined couple payment. Both 2026 geographic schedules have the + # same amounts. + couple_combined = federal.couple + p.amount.couple.INDEPENDENT + individual_combined = federal.individual + p.amount.individual.INDEPENDENT + maximum = where( + other, + individual_combined, + couple_combined + couple * p.ep.couple_increment, + ) + base = where( + couple, + couple_combined, + where( + other, + federal.individual * (1 - federal.one_third_reduction_rate) + + p.amount.individual.HOUSEHOLD_OF_ANOTHER, + individual_combined, + ), + ) + # Derive the partial levels from rule 2784(A) and round to cents. + standard = np.round(base + rate * (maximum - base), 2) + amount = max_(standard - person("vt_aabd_ep_countable_income", period), 0) + # NOTE: The rule 2784 application-month proration is not modeled. + eligible = person_eligible & (independent | ~couple) + return eligible * np.floor(amount) / max_(count, 1) diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_additional_countable_income.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_additional_countable_income.py new file mode 100644 index 00000000000..a0491978763 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_additional_countable_income.py @@ -0,0 +1,12 @@ +from policyengine_us.model_api import * + + +class vt_aabd_ep_additional_countable_income(Variable): + value_type = float + entity = Person + label = "Vermont Essential Person income outside the marital group" + definition_period = MONTH + unit = USD + # PDF pages 48-51 + reference = "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=48" + documentation = "Verified net income of a nonspouse essential person and any parental allocation. Apply the Reach Up exclusions, dependent proration, worker expense allowance and any unused share of the single group general exclusion before entering this amount; do not duplicate the marital group income." diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_countable_income.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_countable_income.py new file mode 100644 index 00000000000..32ddf63a374 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_countable_income.py @@ -0,0 +1,58 @@ +from policyengine_us.model_api import * + + +class vt_aabd_ep_countable_income(Variable): + value_type = float + entity = Person + label = "Vermont Essential Person assistance-group countable income" + definition_period = MONTH + unit = USD + # PDF pages 48-51 + reference = "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=48" + documentation = """ + Income of the marital unit's household members under rule 2786 plus + vt_aabd_ep_additional_countable_income. + NOTE: Reach Up income sources or exclusions without an SSI input are not + modeled. + """ + + def formula(person, period, parameters): + p = parameters(period).gov.states.vt.dcf.aabd.ep + exclusions = parameters(period).gov.ssa.ssi.income.exclusions + abd = person("is_ssi_aged_blind_disabled", period.this_year) + arrangement = person("vt_aabd_living_arrangement", period) + la = arrangement.possible_values + # Rule 2782: the group includes only members living in the same + # household, so a spouse in a facility is left out. + in_household = (arrangement == la.INDEPENDENT) | ( + arrangement == la.HOUSEHOLD_OF_ANOTHER + ) + earned = in_household * max_( + person("ssi_earned_income", period) - p.employment_expense, 0 + ) + abd_earned = person.marital_unit.sum(earned * abd) + other_earned = person.marital_unit.sum(earned * ~abd) + # NOTE: Rule 2786 withholds the $20 exclusion from VA pension only; with + # no pension-specific input, all veterans benefits are treated as pension. + veterans = max_(person("veterans_benefits", period), 0) + unearned = person.marital_unit.sum( + in_household * max_(person("ssi_unearned_income", period) - veterans, 0) + ) + general = exclusions.general + remaining_general = max_(general - unearned, 0) + unearned = max_(unearned - general, 0) + # Apply any remaining group disregard to non-ABD earnings first, + # since only the ABD members receive the half-earnings exclusion. + remaining_general_abd = max_(remaining_general - other_earned, 0) + other_earned = max_(other_earned - remaining_general, 0) + abd_earned = max_(abd_earned - remaining_general_abd - exclusions.earned, 0) * ( + 1 - exclusions.earned_share + ) + needs_based = person.marital_unit.sum( + in_household + * (veterans + person("ssi", period) + person("vt_aabd_federal", period)) + ) + additional = person.marital_unit.max( + max_(person("vt_aabd_ep_additional_countable_income", period), 0) + ) + return unearned + abd_earned + other_earned + needs_based + additional diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_is_spouse.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_is_spouse.py new file mode 100644 index 00000000000..c9ea361fbc5 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_is_spouse.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class vt_aabd_ep_is_spouse(Variable): + value_type = bool + entity = Person + label = "Vermont AABD recipient's Essential Person is an ineligible spouse" + definition_period = MONTH + # PDF pages 38, 44 + reference = "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=38" + documentation = "True for an aged, blind or disabled recipient whose marital unit includes a spouse who is not aged, blind or disabled, unless vt_aabd_ep_nonspouse_essential_person is set." + + def formula(person, period, parameters): + abd = person("is_ssi_aged_blind_disabled", period.this_year) + # A marital unit has at most two members, so a non-ABD member of an + # ABD person's marital unit is that person's spouse. + return ( + abd + & person.marital_unit.any(~abd) + & ~person("vt_aabd_ep_nonspouse_essential_person", period) + ) diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_level.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_level.py new file mode 100644 index 00000000000..02a5e86c2b1 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_level.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class VTAABDEssentialPersonLevel(Enum): + FULL = "Certified full Essential Person care level" + PARTIAL_67 = "Certified 67 percent Essential Person care level" + PARTIAL_34 = "Certified 34 percent Essential Person care level" + NONE = "No certified Essential Person arrangement" + + +class vt_aabd_ep_level(Variable): + value_type = Enum + entity = Person + label = "Vermont certified Essential Person care level" + definition_period = MONTH + possible_values = VTAABDEssentialPersonLevel + default_value = VTAABDEssentialPersonLevel.NONE + # PDF pages 38, 40-41, 44 + reference = "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=38" + documentation = """ + Certified co-resident essential person under rules 2781-2784: full for + an ineligible spouse 55 or older, or required personal care, or seven + homemaker services; 67 percent for a younger spouse unable to work who + provides qualifying care, or a nonspouse providing four to six services; + 34 percent for a nonspouse providing one to three services. Certification + includes benefit pursuit and care preventing institutionalization. Set it + on the aged, blind or disabled recipient, not on the essential person; for + an eligible couple, set it on either spouse and the couple's grant uses the + highest level set on either spouse. + """ diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_nonspouse_essential_person.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_nonspouse_essential_person.py new file mode 100644 index 00000000000..f5e5a229a02 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_nonspouse_essential_person.py @@ -0,0 +1,13 @@ +from policyengine_us.model_api import * + + +class vt_aabd_ep_nonspouse_essential_person(Variable): + value_type = bool + entity = Person + label = "Vermont AABD recipient's certified Essential Person is not the spouse" + definition_period = MONTH + defined_for = StateCode.VT + default_value = False + # PDF pages 38, 40, 42 + reference = "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=38" + documentation = "Set on the aged, blind or disabled recipient whose ineligible spouse lives with them but is not the certified essential person (rule 2781(E)); rule 2782 allows both in one assistance group." diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_passes_state_resource_test.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_passes_state_resource_test.py new file mode 100644 index 00000000000..e8dd64f7ea0 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_passes_state_resource_test.py @@ -0,0 +1,13 @@ +from policyengine_us.model_api import * + + +class vt_aabd_ep_passes_state_resource_test(Variable): + value_type = bool + entity = Person + label = "Vermont Essential Person state resource test passed" + definition_period = MONTH + defined_for = StateCode.VT + default_value = False + # PDF pages 46-48 + reference = "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=46" + documentation = "Resource determination for state-only applicants under rule 2785, including Reach Up resource exclusions as of August 2024, Medicaid limits, and nonspouse dependent proration. SSI/AABD receipt passes the resource test without this input." diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_resources_eligible.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_resources_eligible.py new file mode 100644 index 00000000000..99b4d2bb810 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_ep_resources_eligible.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class vt_aabd_ep_resources_eligible(Variable): + value_type = bool + entity = Person + label = "Vermont Essential Person resource eligibility" + definition_period = MONTH + # PDF pages 46-47 + reference = "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=46" + documentation = "SSI/AABD receipt automatically passes the resource test; otherwise the group passes through vt_aabd_ep_passes_state_resource_test." + + def formula(person, period, parameters): + receives = ( + (person("ssi", period) > 0) + | person("receives_ssi", period) + | (person("vt_aabd_federal", period) > 0) + ) + passes_state_test = person("vt_aabd_ep_passes_state_resource_test", period) + # Rule 2785 tests the assistance group's combined resources once. + return person.marital_unit.any(receives | passes_state_test) diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_federal.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_federal.py new file mode 100644 index 00000000000..7d450b5f1df --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_federal.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.ssa.ssi._ssi_state_supplement import ( + ssi_state_supplement, +) + + +class vt_aabd_federal(Variable): + value_type = float + entity = Person + label = "Vermont federally administered AABD supplement" + definition_period = MONTH + unit = USD + defined_for = StateCode.VT + reference = "https://secure.ssa.gov/poms.nsf/lnx/0501415058#m2" + + def formula(person, period, parameters): + p = parameters(period).gov.states.vt.dcf.aabd.amount + arrangement = person("vt_aabd_living_arrangement", period) + # NOTE: A spouse who declines SSI still makes a joint claim (issue 10243). + return ssi_state_supplement( + person, period, parameters, p.individual[arrangement], p.couple[arrangement] + ) diff --git a/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_living_arrangement.py b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_living_arrangement.py new file mode 100644 index 00000000000..5b63538d64f --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/dcf/aabd/vt_aabd_living_arrangement.py @@ -0,0 +1,72 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.ssa.ssi.eligibility.status.ssi_federal_living_arrangement import ( + SSIFederalLivingArrangement, +) + + +class VTAABDLivingArrangement(Enum): + INDEPENDENT = "Independent living" + HOUSEHOLD_OF_ANOTHER = "Living in another household" + RESIDENTIAL_CARE_LEVEL_III = ( + "Level III residential care with assistive community care (PNMI, code C)" + ) + RESIDENTIAL_CARE_LEVEL_IV = ( + "State-identified Level IV residential or therapeutic care" + ) + CUSTODIAL_CARE = "Qualifying custodial family home" + MEDICAID_INSTITUTION = "Title XIX medical institution" + NONE = "No supplement" + + +class vt_aabd_living_arrangement(Variable): + value_type = Enum + entity = Person + label = "Vermont AABD living arrangement" + definition_period = MONTH + defined_for = StateCode.VT + possible_values = VTAABDLivingArrangement + default_value = VTAABDLivingArrangement.NONE + reference = ( + "https://secure.ssa.gov/poms.nsf/lnx/0501415058#m1", + "https://secure.ssa.gov/poms.nsf/lnx/0501415970BOS", + "https://secure.ssa.gov/poms.nsf/lnx/0501415013BOS", + # PDF pages 28, 31 + "https://outside.vermont.gov/dept/DCF/Shared%20Documents/ESD/Rules/2700-AABD.pdf#page=28", + ) + documentation = """ + Defaults follow federal SSI living arrangements. Residential and custodial + care settings come from vt_aabd_care_setting; those residents are in + federal arrangement A, so do not also set the household-of-another SSI + inputs for them. Independent rates are identical inside and outside + Chittenden County in 2026. Children under 18 in a facility paid by private + health insurance map to independent living (SI BOS01415.970). The + state-administered Level III residential care home with limited nursing + care (rule 2764.2) is not modeled; SI BOS01415.013 reports that all such + homes converted to code C. + NOTE: OS code Y (optional supplementation waived) records a recipient's + choice and is not modeled. + """ + + def formula(person, period, parameters): + federal = person("ssi_federal_living_arrangement", period) + setting = person("vt_aabd_care_setting", period) + settings = setting.possible_values + # POMS SI 01415.058 M.2.a pairs codes C, G and H only with federal + # arrangement A, and code I only with federal arrangement D. + return select( + [ + federal == SSIFederalLivingArrangement.MEDICAL_TREATMENT_FACILITY, + setting == settings.RESIDENTIAL_CARE_LEVEL_III, + setting == settings.RESIDENTIAL_CARE_LEVEL_IV, + setting == settings.CUSTODIAL_CARE, + federal == SSIFederalLivingArrangement.ANOTHER_PERSONS_HOUSEHOLD, + ], + [ + VTAABDLivingArrangement.MEDICAID_INSTITUTION, + VTAABDLivingArrangement.RESIDENTIAL_CARE_LEVEL_III, + VTAABDLivingArrangement.RESIDENTIAL_CARE_LEVEL_IV, + VTAABDLivingArrangement.CUSTODIAL_CARE, + VTAABDLivingArrangement.HOUSEHOLD_OF_ANOTHER, + ], + default=VTAABDLivingArrangement.INDEPENDENT, + ) 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 a473c2b438d..4535a89f3b7 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", + "vt_aabd", # Vermont benefits "ks_sspp", # Kansas benefits "hi_oss", "la_oss", # Louisiana benefits