From 8b0ffcac6b108931f4f63fd800408b2bb8ff7a9c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 17:07:08 -0400 Subject: [PATCH 1/4] Count Colorado's ISM for AND-CS clients in another person's household 9 CCR 2503-5 3.520.79 counts in-kind support and maintenance as unearned income of OAP and AND-CS clients. Since March 2, 2014 an SSI recipient whom SSA charges ISM is charged a matching ISM (3.520.79(B)(6), 3.520.71(E)). AND-CS counted only SSI's presumed maximum value, so a client on SSI's one-third reduction (20 CFR 416.1131) was paid the whole reduction back: $331.33/month in 2026 instead of $20. co_ssa_in_kind_support_and_maintenance matches SSA's ISM for SSI recipients (the one-third reduction or the presumed maximum value, halved for a couple), counts up to the published maximum for other clients (3.520.79(C)), and before 2014 caps shelter at the grant standard's shelter component and counts food supplied in full (3.250.22, 3.250.222). AND-CS reads it; OAP will once its income rewrite lands. Co-Authored-By: Claude Opus 5.5 --- changelog.d/co-adult-financial-ism.fixed.md | 1 + .../co/ssa/ism/component_share/food.yaml | 25 ++ .../co/ssa/ism/component_share/shelter.yaml | 24 ++ .../gov/states/co/ssa/ism/maximum.yaml | 52 +++ .../tests/core/test_co_ism_invariants.py | 225 +++++++++++ ...o_ssa_in_kind_support_and_maintenance.yaml | 372 ++++++++++++++++++ .../state_supplement/co_state_supplement.yaml | 147 ++++++- .../co_ssa_in_kind_support_and_maintenance.py | 122 ++++++ .../state_supplement/_and_cs_client_income.py | 9 +- 9 files changed, 965 insertions(+), 12 deletions(-) create mode 100644 changelog.d/co-adult-financial-ism.fixed.md create mode 100644 policyengine_us/parameters/gov/states/co/ssa/ism/component_share/food.yaml create mode 100644 policyengine_us/parameters/gov/states/co/ssa/ism/component_share/shelter.yaml create mode 100644 policyengine_us/parameters/gov/states/co/ssa/ism/maximum.yaml create mode 100644 policyengine_us/tests/core/test_co_ism_invariants.py create mode 100644 policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml create mode 100644 policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py diff --git a/changelog.d/co-adult-financial-ism.fixed.md b/changelog.d/co-adult-financial-ism.fixed.md new file mode 100644 index 00000000000..9f56ac23d37 --- /dev/null +++ b/changelog.d/co-adult-financial-ism.fixed.md @@ -0,0 +1 @@ +Count Colorado's in-kind support and maintenance (9 CCR 2503-5 3.520.79) in AND-CS income: SSA's one-third reduction for SSI recipients in another person's household from March 2014, Colorado's maximum for clients without SSI, and the pre-2014 shelter and food components. diff --git a/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/food.yaml b/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/food.yaml new file mode 100644 index 00000000000..9f94df74bf5 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/food.yaml @@ -0,0 +1,25 @@ +description: Before 2014 Colorado counted this share of the Aid to the Needy Disabled-Colorado Supplement grant standard as unearned income when food was supplied in full at no cost. + +values: + # The earliest rule text on the Secretary of State's site is the table + # effective January 1, 2004; it is applied from 2002, the first year of the + # grant standard. The table effective January 1, 2014 dropped the food + # component. + 2002-01-01: 0.205 + 2014-01-01: 0 + +metadata: + unit: /1 + period: month + label: Colorado AND-CS food component share before 2014 + reference: + - title: 9 CCR 2503-1, Section 3.250.222, in-kind items other than shelter and utilities + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=584#page=96 + - title: 9 CCR 2503-1, Sections 3.460.43, 3.460.45, AND/AB/SSI-CS components (effective January 1, 2004) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=584#page=124 + - title: 9 CCR 2503-1, Sections 3.460.43, 3.460.45, AND/AB/SSI-CS components (emergency rule effective January 1, 2007) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=2137#page=134 + - title: 9 CCR 2503-2, Section 3.250.222 (effective September 15, 2012) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-2&ruleVersionId=4793#page=32 + - title: 9 CCR 2503-4, Section 3.460.45, grant component table without a food component (effective January 1, 2014) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-4&ruleVersionId=5539#page=13 diff --git a/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/shelter.yaml b/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/shelter.yaml new file mode 100644 index 00000000000..f438a84a882 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/shelter.yaml @@ -0,0 +1,24 @@ +description: Before 2014 Colorado counted up to this share of the Aid to the Needy Disabled-Colorado Supplement grant standard as in-kind support and maintenance for shelter and utilities. + +values: + # The earliest rule text on the Secretary of State's site is the table + # effective January 1, 2004; it is applied from 2002, the first year of the + # grant standard. From 2014 the cap is a dollar amount (ism/maximum.yaml). + 2002-01-01: 0.37 + 2007-01-01: 0.366 + +metadata: + unit: /1 + period: month + label: Colorado AND-CS shelter and utilities component share before 2014 + reference: + - title: 9 CCR 2503-1, Section 3.250.22 (in-kind support and maintenance capped at the shelter and utility component) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=584#page=95 + - title: 9 CCR 2503-1, Sections 3.460.43, 3.460.45, AND/AB/SSI-CS components (effective January 1, 2004) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=584#page=124 + - title: 9 CCR 2503-1, Sections 3.460.43, 3.460.45, AND/AB/SSI-CS components (emergency rule effective January 1, 2007) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=2137#page=134 + - title: 9 CCR 2503-2, Section 3.250.22 (effective September 15, 2012) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-2&ruleVersionId=4793#page=30 + - title: 9 CCR 2503-4, Sections 3.460.43, 3.460.45 (effective September 15, 2012) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-4&ruleVersionId=4795#page=13 diff --git a/policyengine_us/parameters/gov/states/co/ssa/ism/maximum.yaml b/policyengine_us/parameters/gov/states/co/ssa/ism/maximum.yaml new file mode 100644 index 00000000000..36d94d73c50 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/ssa/ism/maximum.yaml @@ -0,0 +1,52 @@ +description: Colorado counts up to this amount of in-kind support and maintenance for shelter as unearned income of an Old Age Pension or Aid to the Needy Disabled-Colorado Supplement client. + +values: + # 9 CCR 2503-5 3.520.79(E): one-third of the SSI benefit standard plus a + # $20 disregard, rounded to the nearest dollar. The rule publishes $284 for + # 2021, a dollar below that formula's $285; the published amounts are used. + 2014-01-01: 260 + 2015-01-01: 264 + 2017-01-01: 265 + 2018-01-01: 270 + 2019-01-01: 277 + 2020-01-01: 281 + 2021-01-01: 284 + 2022-01-01: 300 + 2023-01-01: 325 + 2024-01-01: 334 + 2025-01-01: 342 + 2026-01-01: 351 + +metadata: + unit: currency-USD + period: month + label: Colorado Adult Financial maximum in-kind support and maintenance + reference: + - title: 9 CCR 2503-4, Section 3.460.45, AND/AB/SSI-CS grant component table (effective January 1, 2014) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-4&ruleVersionId=5539#page=13 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(E) (effective March 2, 2014) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=5597#page=44 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(E) (effective January 1, 2015) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=6068#page=45 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(E) (effective November 4, 2016; 2017 amount) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=6928#page=47 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(E) (effective December 1, 2017; 2018 amount) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=7377#page=46 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(E) (effective November 2, 2018; 2019 amount) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=7827#page=46 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(E) (effective January 1, 2020) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=8443#page=46 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(D) (effective January 1, 2021) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=9241#page=61 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(D) (effective January 1, 2022) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=9918#page=61 + - title: 9 CCR 2503-5, Section 3.546(D) (effective January 1, 2023) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=10601#page=73 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(D) (effective March 30, 2024; 2024 amount) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=11381#page=57 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(D) (effective January 1, 2025) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=11744#page=57 + - title: 9 CCR 2503-5, Sections 3.530(B), 3.546(D) (effective March 2, 2026) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=74 + - title: 9 CCR 2503-5, Section 3.520.79(E) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=74 diff --git a/policyengine_us/tests/core/test_co_ism_invariants.py b/policyengine_us/tests/core/test_co_ism_invariants.py new file mode 100644 index 00000000000..856271767e3 --- /dev/null +++ b/policyengine_us/tests/core/test_co_ism_invariants.py @@ -0,0 +1,225 @@ +"""Property tests: Colorado Adult Financial in-kind support and maintenance. + +9 CCR 2503-5 3.520.79 counts in-kind support and maintenance (ISM) as +unearned income of an Old Age Pension or AND-CS client. Under 3.520.79(B)(6) +and 3.520.71(E), an SSI recipient whom SSA charges ISM is charged "a matching +ISM" ("an identical ISM amount"); other clients are counted up to the maximum +ISM (3.520.79(C), $351 in 2026). + +Each case is a disabled SSI client aged 18-59 with a disability benefit and +earnings, single or married to a disabled spouse with the same arrangement, +in 2026. Each case appears twice, as a twin with no in-kind support (N) and +as one receiving it (S): in another person's household under SSI's +one-third reduction, or in their own household with outside shelter support, +optionally of a given value. A third set of people are clients aged 60-64 +without SSI, receiving shelter support of a drawn value. + +1. Matching (differential): an SSI recipient's ISM equals SSA's. Under the + one-third reduction this is the cut SSA made to the benefit rate, the + twins' ssi_amount_if_eligible difference; otherwise it is SSI's presumed + maximum value ISM, halved for a couple. +2. Bounds: ISM is non-negative; a client without SSI is counted no more than + the $351 maximum, and an SSI recipient no more than the presumed maximum + value, one-third of the benefit rate plus $20. +3. No backfill: receiving in-kind support lowers SSI plus AND-CS by at least + SSA's ISM less the client's share of the $20 disregard (3.549(A)(2)), so + AND-CS never restores more of SSA's reduction than that disregard. +4. Monotonicity: for a client without SSI, ISM never falls as the value of + the shelter support rises, and never exceeds that value. +""" + +import numpy as np +import pytest + +from policyengine_us import Simulation + +YEAR = 2026 +PERIOD = f"{YEAR}-01" +TOLERANCE = 0.01 +GROUPS = ("tax_units", "marital_units", "spm_units", "families", "households") + + +def _add_unit(groups, key, members): + for name in GROUPS: + groups[name][f"{name}_{key}"] = {"members": members} + groups["households"][f"households_{key}"]["state_code"] = {YEAR: "CO"} + + +def _arrangement_inputs(arrangement, value): + if arrangement == "another_household": + return { + "ssi_lives_in_another_persons_household": {PERIOD: True}, + "ssi_receives_shelter_from_others_in_household": {PERIOD: True}, + "ssi_others_pay_all_meals": {PERIOD: True}, + } + inputs = {"ssi_receives_outside_shelter_support": {PERIOD: True}} + if value: + inputs["ssi_shelter_support_value"] = {YEAR: value * 12} + return inputs + + +def build_situation(cases, values): + people = {} + groups = {name: {} for name in GROUPS} + for i, c in enumerate(cases): + for twin in "NS": + key = f"{twin}{i}" + members = [] + for role in ("client", "spouse") if c["married"] else ("client",): + name = f"{role}_{key}" + people[name] = { + "age": {YEAR: c[f"{role}_age"]}, + "is_disabled": {YEAR: True}, + "meets_ssi_disability_criteria": {YEAR: True}, + "social_security_disability": {YEAR: c[f"{role}_ssdi"] * 12}, + "employment_income": {YEAR: c[f"{role}_earnings"] * 12}, + } + if twin == "S": + people[name].update( + _arrangement_inputs(c["arrangement"], c["support_value"]) + ) + members.append(name) + if c["married"]: + people[members[0]]["is_tax_unit_head"] = {YEAR: True} + people[members[1]]["is_tax_unit_spouse"] = {YEAR: True} + _add_unit(groups, key, members) + for j, value in enumerate(values): + name = f"older_{j}" + people[name] = { + "age": {YEAR: 62}, + "pension_income": {YEAR: 12_000}, + "ssi_receives_outside_shelter_support": {PERIOD: True}, + "ssi_shelter_support_value": {YEAR: value * 12}, + } + _add_unit(groups, f"older_{j}", [name]) + return {"people": people, **groups} + + +def calculate(cases, values): + simulation = Simulation(situation=build_situation(cases, values)) + names = list(simulation.populations["person"].ids) + results = {} + for variable in ( + "co_ssa_in_kind_support_and_maintenance", + "co_state_supplement", + "ssi", + "ssi_amount_if_eligible", + "ssi_in_kind_support_and_maintenance", + "ssi_couple_computation_applies", + ): + period = YEAR if variable == "ssi_in_kind_support_and_maintenance" else PERIOD + values_ = np.asarray(simulation.calculate(variable, period)) + if variable == "ssi_in_kind_support_and_maintenance": + values_ = values_ / 12 + results[variable] = dict(zip(names, values_)) + p = simulation.tax_benefit_system.parameters(PERIOD) + return results, p + + +def assert_properties(cases, values): + results, p = calculate(cases, values) + ism = results["co_ssa_in_kind_support_and_maintenance"] + supplement = results["co_state_supplement"] + ssi = results["ssi"] + rate = results["ssi_amount_if_eligible"] + maximum = p.gov.states.co.ssa.ism.maximum + individual_pmv = p.gov.ssa.ssi.amount.individual / 3 + 20 + disregard = p.gov.states.co.ssa.state_supplement.income.disregards.unearned + for i, c in enumerate(cases): + roles = ("client", "spouse") if c["married"] else ("client",) + for role in roles: + n, s = f"{role}_N{i}", f"{role}_S{i}" + # 2. Bounds. + assert ism[n] == 0, c + assert -TOLERANCE <= ism[s] <= individual_pmv + TOLERANCE, c + if ssi[s] <= 0: + continue + # 1. Matching. + if c["arrangement"] == "another_household": + expected = rate[n] - rate[s] + else: + expected = results["ssi_in_kind_support_and_maintenance"][s] + if results["ssi_couple_computation_applies"][s]: + expected /= 2 + assert ism[s] == pytest.approx(expected, abs=TOLERANCE), c + # 3. No backfill. + share = disregard / 2 if c["married"] else disregard + drop = ssi[n] + supplement[n] - ssi[s] - supplement[s] + assert drop >= ism[s] - share - TOLERANCE, c + for j, value in enumerate(values): + older = ism[f"older_{j}"] + # 2. Bounds for clients without SSI; 4. ISM is the support value up + # to the maximum, so it rises with the value. + expected = min(value, maximum) if value > 0 else maximum + assert older == pytest.approx(expected, abs=TOLERANCE), value + assert older <= maximum + TOLERANCE, value + ordered = sorted(range(len(values)), key=lambda j: values[j]) + positive = [ism[f"older_{j}"] for j in ordered if values[j] > 0] + assert all(b >= a - TOLERANCE for a, b in zip(positive, positive[1:])) + + +def test_worked_examples(): + """co_state_supplement YAML Cases 28 and 32, monthly: a single client in + another person's household is charged 331.33 and paid $20; each member of + a couple is charged 248.50 and paid 258.50. A client without SSI is + counted the $351 maximum, or a lower support value.""" + single = { + "married": False, + "client_age": 40, + "client_ssdi": 0, + "client_earnings": 0, + "spouse_age": 40, + "spouse_ssdi": 0, + "spouse_earnings": 0, + "arrangement": "another_household", + "support_value": 0, + } + couple = {**single, "married": True, "client_age": 50, "spouse_age": 52} + results, _ = calculate([single, couple], [0, 200, 400]) + ism = results["co_ssa_in_kind_support_and_maintenance"] + supplement = results["co_state_supplement"] + assert ism["client_S0"] == pytest.approx(331.33, abs=TOLERANCE) + assert supplement["client_S0"] == pytest.approx(20, abs=TOLERANCE) + assert ism["client_S1"] == pytest.approx(248.50, abs=TOLERANCE) + assert supplement["spouse_S1"] == pytest.approx(258.50, abs=TOLERANCE) + assert ism["older_0"] == pytest.approx(351, abs=TOLERANCE) + assert ism["older_1"] == pytest.approx(200, abs=TOLERANCE) + assert ism["older_2"] == pytest.approx(351, abs=TOLERANCE) + assert_properties([single, couple], [0, 200, 400]) + + +try: + import hypothesis + import hypothesis.strategies as st +except ImportError: # pragma: no cover + hypothesis = None + +if hypothesis is not None: + # Each example builds a Simulation; on a loaded runner input generation + # can trip the too_slow health check, which says nothing about the model. + SLOW = [hypothesis.HealthCheck.too_slow] + + def amount(high): + return st.one_of(st.just(0), st.integers(min_value=1, max_value=high)) + + @st.composite + def case(draw): + return { + "married": draw(st.booleans()), + "client_age": draw(st.integers(min_value=18, max_value=59)), + "client_ssdi": draw(amount(1_200)), + "client_earnings": draw(amount(1_500)), + "spouse_age": draw(st.integers(min_value=18, max_value=59)), + "spouse_ssdi": draw(amount(1_200)), + "spouse_earnings": draw(amount(1_500)), + "arrangement": draw(st.sampled_from(["another_household", "outside"])), + "support_value": draw(amount(500)), + } + + @hypothesis.settings(max_examples=15, deadline=None, suppress_health_check=SLOW) + @hypothesis.given( + st.lists(case(), min_size=1, max_size=8), + st.lists(amount(500), min_size=1, max_size=6), + ) + def test_properties(cases, values): + assert_properties(cases, values) diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml new file mode 100644 index 00000000000..8759ce9993a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml @@ -0,0 +1,372 @@ +- name: Case 1, an SSI recipient in another person's household is charged SSA's one-third reduction. + # 9 CCR 2503-5 3.520.79(B)(6), 3.520.71(E): a matching ISM. SSA counts + # one-third of the $994 rate as income, 331.33 (20 CFR 416.1131(a)), and + # pays 994 - 331.33 = 662.67. + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + ssi: 662.67 + co_ssa_in_kind_support_and_maintenance: 331.33 + +- name: Case 2, an SSI recipient with outside shelter support is charged SSA's presumed maximum value. + # 994 / 3 + 20 = 351.33 (20 CFR 416.1140), matched under 3.520.79(B)(6). + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_receives_outside_shelter_support: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 351.33 + +- name: Case 3, SSA's lower actual value of shelter support is matched. + # Shelter support worth $200/month is below the 351.33 presumed maximum + # value, so SSA counts 200 and pays 994 - 180 = 814; Colorado matches 200. + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_receives_outside_shelter_support: true + ssi_shelter_support_value: 2_400 + households: + household: + members: [person] + state_code: CO + output: + ssi: 814 + co_ssa_in_kind_support_and_maintenance: 200 + +- name: Case 4, each member of an SSI couple in another person's household is charged half the couple's reduction. + # SSA counts one-third of the $1,491 couple rate, 497, and pays + # 994 / 2 = 497 to each spouse; each spouse matches 248.50. + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + head: + age: 50 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + is_tax_unit_head: true + spouse: + age: 52 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: CO + output: + ssi: [497, 497] + co_ssa_in_kind_support_and_maintenance: [248.5, 248.5] + +- name: Case 5, each member of an SSI couple with outside shelter support is charged half the couple's presumed maximum value. + # 1,491 / 3 + 20 = 517 for the couple, 258.50 each. + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + head: + age: 50 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_receives_outside_shelter_support: true + is_tax_unit_head: true + spouse: + age: 52 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_receives_outside_shelter_support: true + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: [258.5, 258.5] + +- name: Case 6, a client without SSI who pays no shelter costs is counted the maximum ISM. + # Aged 62 and not disabled, so not on SSI. 3.520.79(C)(3): "the current + # maximum ISM amount is counted", $351 in 2026 (3.530(B)). + period: 2026-01 + input: + people: + person: + age: 62 + pension_income: 6_000 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + ssi: 0 + co_ssa_in_kind_support_and_maintenance: 351 + +- name: Case 7, a client without SSI receiving shelter support worth less than the maximum. + # Shelter support worth $200/month is counted, below the $351 maximum. + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + person: + age: 62 + pension_income: 6_000 + ssi_receives_outside_shelter_support: true + ssi_shelter_support_value: 2_400 + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 200 + +- name: Case 8, a client without SSI in subsidized housing is not charged ISM. + # 3.520.79(B)(2). + period: 2026-01 + input: + people: + person: + age: 62 + pension_income: 6_000 + ssi_receives_outside_shelter_support: true + spm_units: + spm_unit: + members: [person] + receives_housing_assistance: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 0 + +- name: Case 9, a client without SSI who owns their home is not charged ISM. + # 3.520.79(B)(1). + period: 2026-01 + input: + people: + person: + age: 62 + pension_income: 6_000 + ssi_receives_outside_shelter_support: true + households: + household: + members: [person] + state_code: CO + tenure_type: OWNED_OUTRIGHT + output: + co_ssa_in_kind_support_and_maintenance: 0 + +- name: Case 10, an SSI recipient who owns their home is charged SSA's ISM anyway. + # 3.520.79(B)(5)-(6): the exemptions apply "unless" SSA charges an ISM, in + # which case the client is charged a matching ISM, 351.33. + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_receives_outside_shelter_support: true + households: + household: + members: [person] + state_code: CO + tenure_type: OWNED_OUTRIGHT + output: + co_ssa_in_kind_support_and_maintenance: 351.33 + +- name: Case 11, a child whom SSI deems from parents is not charged ISM. + # A disabled child living with a parent who has no income: SSI deems + # instead of counting in-kind support (20 CFR 416.1165), so SSA charges + # no ISM and Colorado matches none. + period: 2026-01 + input: + people: + parent: + age: 40 + own_children_in_household: 1 + is_tax_unit_head: true + child: + age: 10 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_receives_shelter_from_others_in_household: true + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: CO + output: + ssi: [0, 994] + co_ssa_in_kind_support_and_maintenance: [0, 0] + +- name: Case 12, no shelter support, no ISM. + period: 2026-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 0 + +- name: Case 13, before the matching rule, January 2014 counts Colorado's maximum. + # 9 CCR 2503-2 3.250.22 caps ISM at the shelter component, $260 from + # January 1, 2014 (9 CCR 2503-4 3.460.45); the client pays no shelter + # costs, so the full $260 is counted. + period: 2014-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 260 + +- name: Case 14, the matching rule takes effect in March 2014. + # January and February 2014 count Colorado's $260 maximum (Case 13). From + # March, 3.520.79(D) (effective March 2, 2014) matches SSA's one-third + # reduction, one-third of the $721 rate, 240.33/month. For the year: + # 2 x 260 + 10 x 240.33 = 2,923.33. + absolute_error_margin: 0.1 + period: 2014 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 2_923.33 + +- name: Case 15, before 2014 free shelter and food count their grant standard components. + # 3.250.22, 3.250.222 and 3.460.45 (components effective 2007): shelter + # and utilities 36.6% and food 20.5% of the $698 standard, + # 255.47 + 143.09 = 398.56. + absolute_error_margin: 0.01 + period: 2012-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 398.56 + +- name: Case 16, before 2014 shelter support alone counts the shelter component. + # 36.6% of $698 = 255.47; food is not supplied in full. + absolute_error_margin: 0.01 + period: 2012-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_receives_outside_shelter_support: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 255.47 + +- name: Case 17, the 2004 components apply before 2007. + # Shelter and utilities 37.0% and food 20.5% of the $604 standard: + # 223.48 + 123.82 = 347.30. + absolute_error_margin: 0.01 + period: 2005-01 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 347.3 diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement.yaml index 35c11d8a0ef..6a7b9292292 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement.yaml @@ -480,10 +480,10 @@ co_state_supplement: 0 - name: Case 28, a single SSI recipient in another person's household. - # SSI pays two-thirds of the $994 rate, 662.67/month (20 CFR 416.1131). - # With no countable income, the grant is 994 - 662.67 = 331.33/month, or - # 3,976/year. SSA's Colorado tables describe the supplement as increased to - # offset the reduced federal payment. + # SSI counts one-third of the $994 rate, 331.33, as income and pays 662.67 + # (20 CFR 416.1131). 3.520.79(B)(6) charges a matching ISM of 331.33, + # counted as unearned income less the $20 disregard (3.549(A)(2)): 311.33. + # Grant 994 - 662.67 - 311.33 = 20/month, or 240/year. absolute_error_margin: 0.1 period: 2026 input: @@ -501,7 +501,9 @@ state_code: CO output: ssi: 7_952 - co_state_supplement: 3_976 + co_ssa_in_kind_support_and_maintenance: 3_976 + co_state_supplement_countable_income: 3_736 + co_state_supplement: 240 - name: Case 29, a client whose working spouse is on Medicaid. # The client has no income; the spouse is not disabled, earns $1,500/month @@ -630,3 +632,138 @@ state_code: CO output: co_state_supplement: 0 + +- name: Case 33, in another person's household with other unearned income. + # $100/month of SSDI: SSI 662.67 - (100 - 20) = 582.67. Colorado counts + # 100 + 331.33 - 20 = 411.33, so total countable income is + # 582.67 + 411.33 = 994, the grant standard: not eligible (3.520.781(A)). + absolute_error_margin: 0.1 + period: 2026 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + social_security_disability: 1_200 + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + ssi: 6_992 + co_state_supplement_countable_income: 4_936 + co_state_supplement: 0 + +- name: Case 34, an SSI couple in another person's household. + # SSI counts one-third of the $1,491 couple rate, 497, and pays 497 to each + # spouse. Each spouse matches half the couple's ISM, 248.50, less their $10 + # share of the disregard: 238.50. Grant 994 - 497 - 238.50 = 258.50/month + # (3,102/year) each. + absolute_error_margin: 0.1 + period: 2026 + input: + people: + head: + age: 50 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + is_tax_unit_head: true + spouse: + age: 52 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: CO + output: + ssi: [5_964, 5_964] + co_state_supplement_countable_income: [2_862, 2_862] + co_state_supplement: [3_102, 3_102] + +- name: Case 35, before 2014 free shelter and food leave no grant. + # 2012: SSI pays two-thirds of $698, 465.33. 9 CCR 2503-2 3.250.22, + # 3.250.222 count shelter and utilities (36.6%) and food (20.5%) of the + # $698 standard, 398.56, less $20: 378.56. 698 - 465.33 - 378.56 < 0. + absolute_error_margin: 0.1 + period: 2012 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + ssi: 5_584 + co_ssa_in_kind_support_and_maintenance: 4_782.7 + co_state_supplement: 0 + +- name: Case 36, 2014 changes from Colorado's maximum ISM to SSA's in March. + # SSI pays two-thirds of $721, 480.67. January and February count the $260 + # maximum, less $20: grant 721 - 480.67 - 240 = 0.33. From March the + # matching ISM is 240.33, less $20: grant 721 - 480.67 - 220.33 = 20. + # For the year: 2 x 0.33 + 10 x 20 = 200.67. There is no payment floor + # before 2026. + absolute_error_margin: 0.1 + period: 2014 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + co_state_supplement: 200.67 + +- name: Case 37, 2025 in another person's household. + # SSI pays two-thirds of $967, 644.67; the matching ISM 322.33 less $20 is + # 302.33. Grant 967 - 644.67 - 302.33 = 20/month, or 240/year. + absolute_error_margin: 0.1 + period: 2025 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + ssi_lives_in_another_persons_household: true + ssi_receives_shelter_from_others_in_household: true + ssi_others_pay_all_meals: true + households: + household: + members: [person] + state_code: CO + output: + ssi: 7_736 + co_state_supplement: 240 diff --git a/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py b/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py new file mode 100644 index 00000000000..743b17d8826 --- /dev/null +++ b/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py @@ -0,0 +1,122 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.ssa.ssi.eligibility.status.ssi_federal_living_arrangement import ( + SSIFederalLivingArrangement, +) +from policyengine_us.variables.household.expense.housing.tenure_type import ( + TenureType, +) + + +def _ism_exempt(person, period): + """9 CCR 2503-5 3.520.79(B)(1)-(3) (9 CCR 2503-1 3.250.22(A), (B), (G) + before 2014): no ISM for a client who resides in and owns their home, + receives subsidized housing or is homeless.""" + tenure = person.household("tenure_type", period.this_year) + owned = (tenure == TenureType.OWNED_OUTRIGHT) | ( + tenure == TenureType.OWNED_WITH_MORTGAGE + ) + in_another_household = person("ssi_lives_in_another_persons_household", period) + owns_home = owned & ~in_another_household + subsidized = person.spm_unit("receives_housing_assistance", period.this_year) + homeless = person.household("is_homeless", period.this_year) + return owns_home | subsidized | homeless + + +def _colorado_shelter_ism(person, period, maximum): + """ISM for shelter as Colorado computes it, up to the maximum. + + 3.520.79(A), (C) (3.250.22 and 3.250.221 before 2014). PolicyEngine has + no input for the client's own shelter payment, the household's fair share + or the market value, so the SSI shelter inputs stand in: a client who + receives shelter from others pays no shelter costs and is counted the + maximum ((C)(3)), unless the value of the shelter support is given, in + which case the lesser of that value and the maximum is counted. + """ + receives_shelter = person( + "ssi_receives_shelter_from_others_in_household", period + ) | person("ssi_receives_outside_shelter_support", period) + value = person("ssi_shelter_support_value", period.this_year) / MONTHS_IN_YEAR + ism = where(value > 0, min_(value, maximum), maximum) + return where(receives_shelter & ~_ism_exempt(person, period), ism, 0) + + +def _ssa_ism(person, period, parameters): + """The in-kind support and maintenance SSA counts for an SSI recipient. + + Under the presumed maximum value rule (20 CFR 416.1140) this is + ssi_in_kind_support_and_maintenance, which holds a couple's presumed + maximum value for each spouse; SSI splits it equally. Under the + one-third reduction rule SSA counts "one-third of the Federal benefit + rate as additional income" (20 CFR 416.1131(a)) for the individual, the + couple or the individual with a deemed spouse; ssi_amount_if_eligible is + the rate after that reduction. + """ + pmv_ism = ( + person("ssi_in_kind_support_and_maintenance", period.this_year) / MONTHS_IN_YEAR + ) + couple_computation = person("ssi_couple_computation_applies", period) + pmv_ism = where(couple_computation, pmv_ism / 2, pmv_ism) + arrangement = person("ssi_federal_living_arrangement", period) + one_third_reduction_applies = ( + arrangement == SSIFederalLivingArrangement.ANOTHER_PERSONS_HOUSEHOLD + ) + rate = parameters(period).gov.ssa.ssi.amount.one_third_reduction_rate + reduced_rate = person("ssi_amount_if_eligible", period) + one_third = reduced_rate * rate / (1 - rate) + return where(one_third_reduction_applies, one_third, pmv_ism) + + +class co_ssa_in_kind_support_and_maintenance(Variable): + value_type = float + entity = Person + label = "Colorado Adult Financial in-kind support and maintenance" + documentation = "In-kind support and maintenance that Colorado counts as unearned income of an Old Age Pension or Aid to the Needy Disabled-Colorado Supplement client." + unit = USD + definition_period = MONTH + defined_for = StateCode.CO + reference = ( + # 3.520.71(E), PDF page 44; 3.520.79, PDF pages 72-74 + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=72", + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=44", + # 3.250.22, 3.250.222 before 2014 + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-2&ruleVersionId=4793#page=30", + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-4&ruleVersionId=4795#page=13", + ) + + def formula_2002(person, period, parameters): + # 9 CCR 2503-1 3.250.22 (9 CCR 2503-2 from September 2012): ISM for + # shelter is capped at the shelter and utility component of the + # grant standard, and 3.250.222 counts food supplied in full at its + # component (3.460.43, 3.460.45). PolicyEngine models OAP only from + # 2023, so the AND-CS standard's components are used. + p = parameters(period).gov.states.co.ssa + standard = p.state_supplement.grant_standard + share = p.ism.component_share + shelter = _colorado_shelter_ism(person, period, share.shelter * standard) + food_supplied_in_full = person("ssi_others_pay_all_meals", period) + food = where( + food_supplied_in_full & ~_ism_exempt(person, period), + share.food * standard, + 0, + ) + return shelter + food + + def formula_2014(person, period, parameters): + # From January 1, 2014 the shelter cap is one-third of the SSI + # benefit standard plus $20 (9 CCR 2503-4 3.460.45), and food is no + # longer counted. + maximum = parameters(period).gov.states.co.ssa.ism.maximum + return _colorado_shelter_ism(person, period, maximum) + + def formula_2014_03(person, period, parameters): + # 3.520.79(B)(6) and 3.520.71(E), effective March 2, 2014 and applied + # from the March 2014 month: an SSI recipient whom SSA charges ISM is + # charged "a matching ISM" ("an identical ISM amount"). With + # PolicyEngine's inputs SSA charges ISM whenever 3.520.79(C) would, + # except to a child in the parental household (whom SSI deems + # instead) or a medical facility resident, so SSI recipients are + # charged SSA's ISM. Other clients' ISM follows 3.520.79(C). + maximum = parameters(period).gov.states.co.ssa.ism.maximum + colorado = _colorado_shelter_ism(person, period, maximum) + receives_ssi = (person("ssi", period) > 0) | person("receives_ssi", period) + return where(receives_ssi, _ssa_ism(person, period, parameters), colorado) diff --git a/policyengine_us/variables/gov/states/co/ssa/state_supplement/_and_cs_client_income.py b/policyengine_us/variables/gov/states/co/ssa/state_supplement/_and_cs_client_income.py index 7e7fd6bc2b7..8532d1b55ee 100644 --- a/policyengine_us/variables/gov/states/co/ssa/state_supplement/_and_cs_client_income.py +++ b/policyengine_us/variables/gov/states/co/ssa/state_supplement/_and_cs_client_income.py @@ -16,13 +16,8 @@ def and_cs_client_income(person, period, parameters): """ p = parameters(period).gov.states.co.ssa.state_supplement.income.disregards earned = person("ssi_earned_income", period) - # A joint couple's in-kind support and maintenance is one presumed - # maximum value for the couple (ssi_pmv_amount), which SSI splits - # equally between the spouses. 3.520.71(E) and 3.520.79(B)(6) charge an - # SSI recipient the same ISM that SSA charges. - ism = person("ssi_in_kind_support_and_maintenance", period) - couple_computation = person("ssi_couple_computation_applies", period) - ism = where(couple_computation, ism / 2, ism) + # 3.520.79 counts in-kind support and maintenance as unearned income. + ism = person("co_ssa_in_kind_support_and_maintenance", period) unearned = person("ssi_unearned_income", period) + ism ssi = person("ssi", period) # 3.549(A)(2)(c)(1): a married client's $20 disregard is split with the From 2080ece183c634b4b4282abd1438292e9269a4c3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 19:40:05 -0400 Subject: [PATCH 2/4] Count Colorado's maximum ISM for non-SSI clients whatever the shelter is worth 3.520.79(C) sets the amount from what the client pays: the maximum less the payment, the lesser of the fair share and the maximum less the payment, or the maximum for a client paying nothing. It never uses the value of the shelter. With no input for the payment, a client receiving shelter from others is taken to pay none and is counted the maximum; ssi_shelter_support_value no longer caps Colorado's own amount (it still feeds SSA's ISM, which SSI recipients match). Applies the GPT-6.1 Sol review of 9cb7d30b4b. Co-Authored-By: Claude Opus 5.5 --- .../tests/core/test_co_ism_invariants.py | 30 ++++++++----------- ...o_ssa_in_kind_support_and_maintenance.yaml | 9 +++--- .../co_ssa_in_kind_support_and_maintenance.py | 20 ++++++------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/policyengine_us/tests/core/test_co_ism_invariants.py b/policyengine_us/tests/core/test_co_ism_invariants.py index 856271767e3..647f03f95e1 100644 --- a/policyengine_us/tests/core/test_co_ism_invariants.py +++ b/policyengine_us/tests/core/test_co_ism_invariants.py @@ -11,21 +11,21 @@ in 2026. Each case appears twice, as a twin with no in-kind support (N) and as one receiving it (S): in another person's household under SSI's one-third reduction, or in their own household with outside shelter support, -optionally of a given value. A third set of people are clients aged 60-64 +optionally of a given value. A third set of people are clients aged 62 without SSI, receiving shelter support of a drawn value. 1. Matching (differential): an SSI recipient's ISM equals SSA's. Under the one-third reduction this is the cut SSA made to the benefit rate, the twins' ssi_amount_if_eligible difference; otherwise it is SSI's presumed maximum value ISM, halved for a couple. -2. Bounds: ISM is non-negative; a client without SSI is counted no more than - the $351 maximum, and an SSI recipient no more than the presumed maximum - value, one-third of the benefit rate plus $20. +2. Bounds: ISM is non-negative, and an SSI recipient's is no more than the + presumed maximum value, one-third of the benefit rate plus $20. 3. No backfill: receiving in-kind support lowers SSI plus AND-CS by at least SSA's ISM less the client's share of the $20 disregard (3.549(A)(2)), so AND-CS never restores more of SSA's reduction than that disregard. -4. Monotonicity: for a client without SSI, ISM never falls as the value of - the shelter support rises, and never exceeds that value. +4. Payment, not value: a client without SSI who receives shelter from + others, and so pays none (3.520.79(C)(3)), is counted the $351 maximum + whatever the shelter is worth. """ import numpy as np @@ -147,22 +147,16 @@ def assert_properties(cases, values): drop = ssi[n] + supplement[n] - ssi[s] - supplement[s] assert drop >= ism[s] - share - TOLERANCE, c for j, value in enumerate(values): - older = ism[f"older_{j}"] - # 2. Bounds for clients without SSI; 4. ISM is the support value up - # to the maximum, so it rises with the value. - expected = min(value, maximum) if value > 0 else maximum - assert older == pytest.approx(expected, abs=TOLERANCE), value - assert older <= maximum + TOLERANCE, value - ordered = sorted(range(len(values)), key=lambda j: values[j]) - positive = [ism[f"older_{j}"] for j in ordered if values[j] > 0] - assert all(b >= a - TOLERANCE for a, b in zip(positive, positive[1:])) + # 4. Payment, not value. + assert ism[f"older_{j}"] == pytest.approx(maximum, abs=TOLERANCE), value def test_worked_examples(): """co_state_supplement YAML Cases 28 and 32, monthly: a single client in another person's household is charged 331.33 and paid $20; each member of - a couple is charged 248.50 and paid 258.50. A client without SSI is - counted the $351 maximum, or a lower support value.""" + a couple is charged 248.50 and paid 258.50. A client without SSI who + pays nothing is counted the $351 maximum, whatever the support is + worth.""" single = { "married": False, "client_age": 40, @@ -183,7 +177,7 @@ def test_worked_examples(): assert ism["client_S1"] == pytest.approx(248.50, abs=TOLERANCE) assert supplement["spouse_S1"] == pytest.approx(258.50, abs=TOLERANCE) assert ism["older_0"] == pytest.approx(351, abs=TOLERANCE) - assert ism["older_1"] == pytest.approx(200, abs=TOLERANCE) + assert ism["older_1"] == pytest.approx(351, abs=TOLERANCE) assert ism["older_2"] == pytest.approx(351, abs=TOLERANCE) assert_properties([single, couple], [0, 200, 400]) diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml index 8759ce9993a..e8a007f10aa 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml @@ -148,9 +148,10 @@ ssi: 0 co_ssa_in_kind_support_and_maintenance: 351 -- name: Case 7, a client without SSI receiving shelter support worth less than the maximum. - # Shelter support worth $200/month is counted, below the $351 maximum. - absolute_error_margin: 0.01 +- name: Case 7, a client without SSI who pays nothing is counted the maximum whatever the shelter is worth. + # Others supply shelter worth $200/month in full. 3.520.79(C)(3) counts + # "the current maximum ISM amount", $351, for a client "paying no shelter + # costs"; the value of the shelter does not enter Colorado's calculation. period: 2026-01 input: people: @@ -164,7 +165,7 @@ members: [person] state_code: CO output: - co_ssa_in_kind_support_and_maintenance: 200 + co_ssa_in_kind_support_and_maintenance: 351 - name: Case 8, a client without SSI in subsidized housing is not charged ISM. # 3.520.79(B)(2). diff --git a/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py b/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py index 743b17d8826..aa756575825 100644 --- a/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py +++ b/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py @@ -23,21 +23,21 @@ def _ism_exempt(person, period): def _colorado_shelter_ism(person, period, maximum): - """ISM for shelter as Colorado computes it, up to the maximum. + """ISM for shelter as Colorado computes it. - 3.520.79(A), (C) (3.250.22 and 3.250.221 before 2014). PolicyEngine has - no input for the client's own shelter payment, the household's fair share - or the market value, so the SSI shelter inputs stand in: a client who - receives shelter from others pays no shelter costs and is counted the - maximum ((C)(3)), unless the value of the shelter support is given, in - which case the lesser of that value and the maximum is counted. + 3.520.79(A), (C) (3.250.22 and 3.250.221 before 2014). Colorado's amount + depends on what the client pays, not on what the shelter is worth: the + maximum less the client's payment ((C)(1)), the lesser of the fair share + and the maximum less the payment ((C)(2)), or, for a client "paying no + shelter costs", the maximum ((C)(3)). PolicyEngine has no input for the + client's payment, the fair share or the market value, so a client who + receives shelter from others is taken to pay none and is counted the + maximum. """ receives_shelter = person( "ssi_receives_shelter_from_others_in_household", period ) | person("ssi_receives_outside_shelter_support", period) - value = person("ssi_shelter_support_value", period.this_year) / MONTHS_IN_YEAR - ism = where(value > 0, min_(value, maximum), maximum) - return where(receives_shelter & ~_ism_exempt(person, period), ism, 0) + return where(receives_shelter & ~_ism_exempt(person, period), maximum, 0) def _ssa_ism(person, period, parameters): From 321beb0980dad022abbdfd031d592b1050704af4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 20:31:52 -0400 Subject: [PATCH 3/4] Exempt medical confinement, start the pre-2014 ISM in 2007, floor-aware invariant Applies the GPT-6.1 Sol review (round 2) of 0c4370a9d4: - 3.520.786(B) (3.250.32 before 2014) exempts room and board furnished during medical confinement and paid by a third party, so a client in a medical treatment facility is counted no ISM. - The January 2004 AND/SSI-CS table prints a $513 maximum grant against a $601 grant standard, so the base of its 37% shelter and 20.5% food components is unsettled. The pre-2014 formula now starts with the 2007 table (36.6%, 20.5%); 2002-2006 are left for the standards audit. - The no-backfill invariant subtracts any state supplementary payment that raises a grant to the $13 floor; the review's counterexample (a couple earning $1,145 and $106 a month) is pinned as a Hypothesis example. Co-Authored-By: Claude Opus 5.5 --- .../co/ssa/ism/component_share/food.yaml | 14 ++++----- .../co/ssa/ism/component_share/shelter.yaml | 14 ++++----- .../tests/core/test_co_ism_invariants.py | 30 ++++++++++++++++--- ...o_ssa_in_kind_support_and_maintenance.yaml | 30 +++++++++++++++---- .../co_ssa_in_kind_support_and_maintenance.py | 24 ++++++++++----- 5 files changed, 79 insertions(+), 33 deletions(-) diff --git a/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/food.yaml b/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/food.yaml index 9f94df74bf5..bf70dd4f1f7 100644 --- a/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/food.yaml +++ b/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/food.yaml @@ -1,11 +1,9 @@ description: Before 2014 Colorado counted this share of the Aid to the Needy Disabled-Colorado Supplement grant standard as unearned income when food was supplied in full at no cost. values: - # The earliest rule text on the Secretary of State's site is the table - # effective January 1, 2004; it is applied from 2002, the first year of the - # grant standard. The table effective January 1, 2014 dropped the food - # component. - 2002-01-01: 0.205 + # Applied from 2007, as for the shelter share. The table effective + # January 1, 2014 dropped the food component. + 2007-01-01: 0.205 2014-01-01: 0 metadata: @@ -13,10 +11,8 @@ metadata: period: month label: Colorado AND-CS food component share before 2014 reference: - - title: 9 CCR 2503-1, Section 3.250.222, in-kind items other than shelter and utilities - href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=584#page=96 - - title: 9 CCR 2503-1, Sections 3.460.43, 3.460.45, AND/AB/SSI-CS components (effective January 1, 2004) - href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=584#page=124 + - title: 9 CCR 2503-1, Section 3.250.222 (effective January 1, 2008), in-kind items other than shelter and utilities + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=2137#page=103 - title: 9 CCR 2503-1, Sections 3.460.43, 3.460.45, AND/AB/SSI-CS components (emergency rule effective January 1, 2007) href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=2137#page=134 - title: 9 CCR 2503-2, Section 3.250.222 (effective September 15, 2012) diff --git a/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/shelter.yaml b/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/shelter.yaml index f438a84a882..b58349b3220 100644 --- a/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/shelter.yaml +++ b/policyengine_us/parameters/gov/states/co/ssa/ism/component_share/shelter.yaml @@ -1,10 +1,10 @@ description: Before 2014 Colorado counted up to this share of the Aid to the Needy Disabled-Colorado Supplement grant standard as in-kind support and maintenance for shelter and utilities. values: - # The earliest rule text on the Secretary of State's site is the table - # effective January 1, 2004; it is applied from 2002, the first year of the - # grant standard. From 2014 the cap is a dollar amount (ism/maximum.yaml). - 2002-01-01: 0.37 + # Applied from 2007, the first table whose base is the grant standard + # without a conflicting figure (the January 1, 2004 table had 37.0% and + # printed a $513 maximum grant). From 2014 the cap is a dollar amount + # (ism/maximum.yaml). 2007-01-01: 0.366 metadata: @@ -12,10 +12,8 @@ metadata: period: month label: Colorado AND-CS shelter and utilities component share before 2014 reference: - - title: 9 CCR 2503-1, Section 3.250.22 (in-kind support and maintenance capped at the shelter and utility component) - href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=584#page=95 - - title: 9 CCR 2503-1, Sections 3.460.43, 3.460.45, AND/AB/SSI-CS components (effective January 1, 2004) - href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=584#page=124 + - title: 9 CCR 2503-1, Section 3.250.22 (effective January 1, 2008; ISM capped at the shelter and utility component) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=2137#page=101 - title: 9 CCR 2503-1, Sections 3.460.43, 3.460.45, AND/AB/SSI-CS components (emergency rule effective January 1, 2007) href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-1&ruleVersionId=2137#page=134 - title: 9 CCR 2503-2, Section 3.250.22 (effective September 15, 2012) diff --git a/policyengine_us/tests/core/test_co_ism_invariants.py b/policyengine_us/tests/core/test_co_ism_invariants.py index 647f03f95e1..0fe07ab89f3 100644 --- a/policyengine_us/tests/core/test_co_ism_invariants.py +++ b/policyengine_us/tests/core/test_co_ism_invariants.py @@ -21,8 +21,10 @@ 2. Bounds: ISM is non-negative, and an SSI recipient's is no more than the presumed maximum value, one-third of the benefit rate plus $20. 3. No backfill: receiving in-kind support lowers SSI plus AND-CS by at least - SSA's ISM less the client's share of the $20 disregard (3.549(A)(2)), so - AND-CS never restores more of SSA's reduction than that disregard. + SSA's ISM less the client's share of the $20 disregard (3.549(A)(2)) and + less any state supplementary payment that raises a grant to the $13 + floor (3.549(B)), so AND-CS restores no more of SSA's reduction than + those. 4. Payment, not value: a client without SSI who receives shelter from others, and so pays none (3.520.79(C)(3)), is counted the $351 maximum whatever the shelter is worth. @@ -102,6 +104,7 @@ def calculate(cases, values): for variable in ( "co_ssa_in_kind_support_and_maintenance", "co_state_supplement", + "co_state_supplement_countable_income", "ssi", "ssi_amount_if_eligible", "ssi_in_kind_support_and_maintenance", @@ -124,7 +127,9 @@ def assert_properties(cases, values): rate = results["ssi_amount_if_eligible"] maximum = p.gov.states.co.ssa.ism.maximum individual_pmv = p.gov.ssa.ssi.amount.individual / 3 + 20 - disregard = p.gov.states.co.ssa.state_supplement.income.disregards.unearned + and_cs = p.gov.states.co.ssa.state_supplement + disregard = and_cs.income.disregards.unearned + countable = results["co_state_supplement_countable_income"] for i, c in enumerate(cases): roles = ("client", "spouse") if c["married"] else ("client",) for role in roles: @@ -144,8 +149,10 @@ def assert_properties(cases, values): assert ism[s] == pytest.approx(expected, abs=TOLERANCE), c # 3. No backfill. share = disregard / 2 if c["married"] else disregard + grant = max(0, and_cs.grant_standard - ssi[s] - countable[s]) + floor_top_up = supplement[s] - grant if supplement[s] > 0 else 0 drop = ssi[n] + supplement[n] - ssi[s] - supplement[s] - assert drop >= ism[s] - share - TOLERANCE, c + assert drop >= ism[s] - share - floor_top_up - TOLERANCE, c for j, value in enumerate(values): # 4. Payment, not value. assert ism[f"older_{j}"] == pytest.approx(maximum, abs=TOLERANCE), value @@ -210,7 +217,22 @@ def case(draw): "support_value": draw(amount(500)), } + # Review counterexample: the supported spouse's $10 grant is raised to + # the $13 floor. + FLOOR_CASE = { + "married": True, + "client_age": 18, + "client_ssdi": 0, + "client_earnings": 1_145, + "spouse_age": 18, + "spouse_ssdi": 0, + "spouse_earnings": 106, + "arrangement": "another_household", + "support_value": 0, + } + @hypothesis.settings(max_examples=15, deadline=None, suppress_health_check=SLOW) + @hypothesis.example(cases=[FLOOR_CASE], values=[0]) @hypothesis.given( st.lists(case(), min_size=1, max_size=8), st.lists(amount(500), min_size=1, max_size=6), diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml index e8a007f10aa..82670d6fa97 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml @@ -351,11 +351,11 @@ output: co_ssa_in_kind_support_and_maintenance: 255.47 -- name: Case 17, the 2004 components apply before 2007. - # Shelter and utilities 37.0% and food 20.5% of the $604 standard: - # 223.48 + 123.82 = 347.30. +- name: Case 17, the 2007 components. + # Shelter and utilities 36.6% and food 20.5% of the $648 standard + # (components effective January 1, 2007): 237.17 + 132.84 = 370.01. absolute_error_margin: 0.01 - period: 2005-01 + period: 2007-01 input: people: person: @@ -370,4 +370,24 @@ members: [person] state_code: CO output: - co_ssa_in_kind_support_and_maintenance: 347.3 + co_ssa_in_kind_support_and_maintenance: 370.01 + +- name: Case 18, room and board during medical confinement is not counted. + # A client without SSI in a hospital whose room and board Medicaid pays. + # 3.520.786(B) exempts "room and board furnished during medical + # confinement and paid for by a third party". + period: 2026-01 + input: + people: + person: + age: 62 + pension_income: 6_000 + ssi_receives_outside_shelter_support: true + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + households: + household: + members: [person] + state_code: CO + output: + co_ssa_in_kind_support_and_maintenance: 0 diff --git a/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py b/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py index aa756575825..042bf679e81 100644 --- a/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py +++ b/policyengine_us/variables/gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.py @@ -8,9 +8,12 @@ def _ism_exempt(person, period): - """9 CCR 2503-5 3.520.79(B)(1)-(3) (9 CCR 2503-1 3.250.22(A), (B), (G) - before 2014): no ISM for a client who resides in and owns their home, - receives subsidized housing or is homeless.""" + """No ISM for a client who resides in and owns their home, receives + subsidized housing or is homeless (9 CCR 2503-5 3.520.79(B)(1)-(3); + 9 CCR 2503-1 3.250.22(A), (B), (G) before 2014), or whose room and board + is furnished during medical confinement: exempt income when a third + party pays, and not in-kind support when the client does (3.520.786(B); + 3.250.32 before 2014).""" tenure = person.household("tenure_type", period.this_year) owned = (tenure == TenureType.OWNED_OUTRIGHT) | ( tenure == TenureType.OWNED_WITH_MORTGAGE @@ -19,7 +22,8 @@ def _ism_exempt(person, period): owns_home = owned & ~in_another_household subsidized = person.spm_unit("receives_housing_assistance", period.this_year) homeless = person.household("is_homeless", period.this_year) - return owns_home | subsidized | homeless + medical_confinement = person("ssi_lives_in_medical_treatment_facility", period) + return owns_home | subsidized | homeless | medical_confinement def _colorado_shelter_ism(person, period, maximum): @@ -78,17 +82,23 @@ class co_ssa_in_kind_support_and_maintenance(Variable): # 3.520.71(E), PDF page 44; 3.520.79, PDF pages 72-74 "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=72", "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=44", - # 3.250.22, 3.250.222 before 2014 + # 3.520.786(B), PDF page 71 + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=71", + # 3.250.22, 3.250.222, 3.250.32 before 2014 "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-2&ruleVersionId=4793#page=30", "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-4&ruleVersionId=4795#page=13", ) - def formula_2002(person, period, parameters): + def formula_2007(person, period, parameters): # 9 CCR 2503-1 3.250.22 (9 CCR 2503-2 from September 2012): ISM for # shelter is capped at the shelter and utility component of the # grant standard, and 3.250.222 counts food supplied in full at its # component (3.460.43, 3.460.45). PolicyEngine models OAP only from - # 2023, so the AND-CS standard's components are used. + # 2023, so the AND-CS standard's components are used. The rule + # counted ISM before 2007 too, but the table effective January 1, + # 2004 prints a $513 AND/SSI-CS maximum grant where the grant + # standard is $601, so the base for 2002-2006 is unsettled and those + # years are not modeled. p = parameters(period).gov.states.co.ssa standard = p.state_supplement.grant_standard share = p.ism.component_share From 8e69c97d13b465850af8f0c1726ad5ea6a2b426f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 20:44:33 -0400 Subject: [PATCH 4/4] Point the ISM worked-example docstring at the renumbered couple case Co-Authored-By: Claude Opus 5.5 --- policyengine_us/tests/core/test_co_ism_invariants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/tests/core/test_co_ism_invariants.py b/policyengine_us/tests/core/test_co_ism_invariants.py index 0fe07ab89f3..340b0f6e34f 100644 --- a/policyengine_us/tests/core/test_co_ism_invariants.py +++ b/policyengine_us/tests/core/test_co_ism_invariants.py @@ -159,7 +159,7 @@ def assert_properties(cases, values): def test_worked_examples(): - """co_state_supplement YAML Cases 28 and 32, monthly: a single client in + """co_state_supplement YAML Cases 28 and 34, monthly: a single client in another person's household is charged 331.33 and paid $20; each member of a couple is charged 248.50 and paid 258.50. A client without SSI who pays nothing is counted the $351 maximum, whatever the support is