diff --git a/changelog.d/co-and-cs-payment-floor-3549.fixed.md b/changelog.d/co-and-cs-payment-floor-3549.fixed.md new file mode 100644 index 00000000000..8e673dd8095 --- /dev/null +++ b/changelog.d/co-and-cs-payment-floor-3549.fixed.md @@ -0,0 +1 @@ +Apply Colorado's $13 AND-CS payment floor from January 2026, and count income for every AND-CS client under 9 CCR 2503-5 3.549: no unearned disregard for SSI-only clients, spousal and parental deeming without SSI's threshold, and the exemption for spouses and parents on SSI, Old Age Pension or Medicaid. diff --git a/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/deeming/assistance_exemption_applies.yaml b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/deeming/assistance_exemption_applies.yaml new file mode 100644 index 00000000000..272b404961d --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/deeming/assistance_exemption_applies.yaml @@ -0,0 +1,18 @@ +description: Colorado does not deem income to an Aid to the Needy Disabled-Colorado Supplement client from a spouse or parent who receives Adult Financial grant payments, SSI or Medicaid with income no greater than the SSI limit, if this is true. + +values: + # Before March 2020, 3.545(F) excluded only the income from those benefits; + # the recipient's other income was deemed. + 2002-01-01: false + 2020-03-01: true + +metadata: + unit: bool + label: Colorado AND-CS deeming exemption for assistance recipients + reference: + - title: 9 CCR 2503-5, Section 3.549(F) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=95 + - title: 9 CCR 2503-5, Section 3.549(F) (effective March 1, 2020) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=8577#page=77 + - title: 9 CCR 2503-5, Section 3.545(F) (effective January 1, 2020) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=8443#page=63 diff --git a/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/earned.yaml b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/earned.yaml new file mode 100644 index 00000000000..11d86ee6096 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/earned.yaml @@ -0,0 +1,14 @@ +description: Colorado deducts this amount from monthly gross earned income when counting income for the Aid to the Needy Disabled-Colorado Supplement. + +values: + 2002-01-01: 65 + +metadata: + unit: currency-USD + period: month + label: Colorado AND-CS earned income disregard + reference: + - title: "9 CCR 2503-5, Section 3.549(A)(1)(a), (C)(2) #page 93-94" + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=93 + - title: SSA State Assistance Programs for SSI Recipients, January 2002 - Colorado (income exclusions same as federal) + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2002/co.html diff --git a/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/earned_share.yaml b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/earned_share.yaml new file mode 100644 index 00000000000..b9546a5978c --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/earned_share.yaml @@ -0,0 +1,13 @@ +description: Colorado counts this share of earned income above the flat disregard for the Aid to the Needy Disabled-Colorado Supplement. + +values: + 2002-01-01: 0.5 + +metadata: + unit: /1 + label: Colorado AND-CS countable share of earned income + reference: + - title: "9 CCR 2503-5, Section 3.549(A)(1)(b), (C)(3) #page 94" + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=94 + - title: SSA State Assistance Programs for SSI Recipients, January 2002 - Colorado (income exclusions same as federal) + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2002/co.html diff --git a/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/unearned.yaml b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/unearned.yaml new file mode 100644 index 00000000000..635a0c1e629 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/income/disregards/unearned.yaml @@ -0,0 +1,14 @@ +description: Colorado disregards this amount of unearned income other than SSI, shared by a married couple, for the Aid to the Needy Disabled-Colorado Supplement. + +values: + 2002-01-01: 20 + +metadata: + unit: currency-USD + period: month + label: Colorado AND-CS unearned income disregard + reference: + - title: "9 CCR 2503-5, Section 3.549(A)(2)(c), (D)(2) #page 94-95" + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=94 + - title: SSA State Assistance Programs for SSI Recipients, January 2002 - Colorado (income exclusions same as federal) + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2002/co.html diff --git a/policyengine_us/parameters/gov/states/co/ssa/state_supplement/payment_floor.yaml b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/payment_floor.yaml new file mode 100644 index 00000000000..9153a6d5058 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/ssa/state_supplement/payment_floor.yaml @@ -0,0 +1,23 @@ +description: Colorado raises an Aid to the Needy Disabled-Colorado Supplement grant below this amount to it with a state supplementary payment. + +values: + # Before 2026 Colorado met the SSI pass-along requirement by maintaining + # total expenditures, with irregular lump-sum payments and no payment floor. + 2002-01-01: 0 + 2026-01-01: 13 + +metadata: + unit: currency-USD + period: month + label: Colorado AND-CS payment floor standard + reference: + - title: 9 CCR 2503-5, Section 3.546(E), AND-CS payment floor standard + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=92 + - title: 9 CCR 2503-5, Section 3.546(E) (emergency rule effective January 1, 2026) + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12318#page=74 + - title: 9 CCR 2503-5, Section 3.546(C) (effective March 2, 2025), MOE payments without a payment floor + href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=11861#page=73 + - title: "Colorado State Board of Human Services, Adult Financial Payment Floor and COLA 2026, rulemaking packet 2025-00596 #page 4,9" + href: https://www.sos.state.co.us/CCR/Upload/AGORequestEmergency/RegulatoryAnalysisAttachment2025-00596.pdf#page=4 + - title: SSA State Assistance Programs for SSI Recipients, January 2011 - Colorado (passalong method) + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/co.html diff --git a/policyengine_us/tests/core/test_co_and_cs_invariants.py b/policyengine_us/tests/core/test_co_and_cs_invariants.py new file mode 100644 index 00000000000..db181d60570 --- /dev/null +++ b/policyengine_us/tests/core/test_co_and_cs_invariants.py @@ -0,0 +1,291 @@ +"""Property tests: Colorado AND-CS countable income, deeming and payment floor. + +9 CCR 2503-5 3.549(A) counts an AND-CS client's own income; 3.549(C)-(D) +deem part of a spouse's income to the client, except under 3.549(F) when the +spouse receives SSI, Old Age Pension or Medicaid with income within the SSI +limit; 3.520.781(A) makes a client whose total countable income (SSI +included) equals or exceeds the grant standard ineligible; and 3.546(E), +3.549(B) raise a grant below the $13 payment floor standard to it. + +Each case is a disabled client aged 18-59 in their own household, with +pension, interest and earnings, optionally receiving outside shelter +support, and either single or married to a spouse who is not disabled and +has earnings and a pension. Each married case appears four times: (A) the +spouse is enrolled in Medicaid, (B) the spouse is not, (C) as B with extra +spousal earnings, and (D) as B with the tax-unit roles swapped. Income can +be high enough that the client gets no SSI; 3.546 then makes them no AND-CS +client, and countable income, defined only for clients, is compared only +between arrangements in which the client is one. For 2026: + +1. Payment: the supplement is the grant standard less total countable income + when that is positive, raised to the floor if below it, and zero otherwise; + so it is zero or between the floor and the standard. +2. Singles: since 2012 the grant standard has equaled the SSI individual + benefit rate, and for a client 3.549(A) never counts less than SSI does, + so a single client in their own household is never paid. Where the client has no + earnings or at least $20 of unearned income other than SSI, 3.549(A) and + SSI's exclusions (20 CFR 416.1112, 416.1124) count the same income. +3. Exemption: Medicaid enrollment of a spouse within the SSI limit can only + lower the client's countable income and raise the supplement. +4. Monotonicity: for a client without in-kind support, more earnings for a + non-exempt spouse never lower the client's countable income or raise the + supplement. (In-kind support is excluded: 3.520.79(B)(6) charges a client + the ISM SSA charges, and SSA's presumed maximum value halves once spousal + deeming starts, so crossing SSI's deeming threshold lowers the client's + ISM by about as much as the deemed earnings rise.) +5. Role invariance: the client's countable income and supplement do not + depend on which spouse is the tax-unit head. + +YAML states the worked cases; Python is needed to compare arrangements of +the same household in one simulation. +""" + +import numpy as np +import pytest + +from policyengine_us import Simulation + +YEAR = 2026 +TOLERANCE = 0.01 +ARRANGEMENTS = "ABCD" + + +def build_situation(cases): + people, groups = ( + {}, + { + name: {} + for name in ( + "tax_units", + "marital_units", + "spm_units", + "families", + "households", + ) + }, + ) + for i, c in enumerate(cases): + for arrangement in ARRANGEMENTS if c["married"] else "A": + key = f"{arrangement}{i}" + client, spouse = f"client_{key}", f"spouse_{key}" + people[client] = { + "age": {YEAR: c["client_age"]}, + "is_disabled": {YEAR: True}, + "meets_ssi_disability_criteria": {YEAR: True}, + "pension_income": {YEAR: c["client_pension"]}, + "interest_income": {YEAR: c["client_interest"]}, + "employment_income": {YEAR: c["client_earnings"]}, + "ssi_receives_outside_shelter_support": {YEAR: c["shelter_support"]}, + # Set for everyone: an input for some people gives the others + # its default (false), not its formula. + "co_state_supplement_medicaid_enrolled": {YEAR: True}, + } + members = [client] + if c["married"]: + extra = c["extra_spouse_earnings"] if arrangement == "C" else 0 + people[spouse] = { + "age": {YEAR: c["spouse_age"]}, + "employment_income": {YEAR: c["spouse_earnings"] + extra}, + "pension_income": {YEAR: c["spouse_pension"]}, + "co_state_supplement_medicaid_enrolled": {YEAR: arrangement == "A"}, + } + members = [spouse, client] if arrangement == "D" else [client, spouse] + head, partner = members + people[head]["is_tax_unit_head"] = {YEAR: True} + people[head]["is_tax_unit_spouse"] = {YEAR: False} + people[partner]["is_tax_unit_head"] = {YEAR: False} + people[partner]["is_tax_unit_spouse"] = {YEAR: True} + for name, group in groups.items(): + group[f"{name}_{key}"] = {"members": members} + groups["households"][f"households_{key}"]["state_code"] = {YEAR: "CO"} + return {"people": people, **groups} + + +def calculate(cases): + """Arrays indexed by person name for the client of every arrangement.""" + simulation = Simulation(situation=build_situation(cases)) + names = list(simulation.populations["person"].ids) + results = {} + for variable, period in ( + ("co_state_supplement", YEAR), + ("co_state_supplement_countable_income", YEAR), + ("co_state_supplement_eligible", f"{YEAR}-01"), + ("ssi", YEAR), + ("ssi_countable_income", YEAR), + ): + values = np.asarray(simulation.calculate(variable, period)) + results[variable] = dict(zip(names, values)) + parameters = simulation.tax_benefit_system.parameters(f"{YEAR}-01") + p = parameters.gov.states.co.ssa.state_supplement + return results, p.grant_standard, p.payment_floor + + +def assert_properties(cases): + results, standard, floor = calculate(cases) + supplement = results["co_state_supplement"] + countable = results["co_state_supplement_countable_income"] + eligible = results["co_state_supplement_eligible"] + ssi = results["ssi"] + for i, c in enumerate(cases): + for arrangement in ARRANGEMENTS if c["married"] else "A": + client = f"client_{arrangement}{i}" + # Monthly amounts: the inputs and the program are constant + # within the year. + total = (ssi[client] + countable[client]) / 12 + paid = supplement[client] / 12 + # 1. Payment rule and bounds. + if eligible[client] and total < standard - TOLERANCE: + expected = max(standard - total, floor) + assert paid == pytest.approx(expected, abs=TOLERANCE), c + assert floor - TOLERANCE <= paid <= standard + TOLERANCE, c + elif eligible[client] and total > standard + TOLERANCE: + assert paid == 0, c + client = f"client_A{i}" + if not c["married"]: + # 2. Singles. + assert supplement[client] == 0, c + if eligible[client]: + federal = results["ssi_countable_income"][client] + assert countable[client] >= federal - TOLERANCE, c + other_unearned = c["client_pension"] + c["client_interest"] + if c["client_earnings"] == 0 or other_unearned >= 20 * 12: + assert countable[client] == pytest.approx(federal, abs=TOLERANCE), c + continue + a, b, c_, d = (f"client_{x}{i}" for x in ARRANGEMENTS) + # The spouse's Medicaid and the head role do not change the client's + # SSI, so A, B and D have the same clients. + assert eligible[a] == eligible[b] == eligible[d], c + # 3. The Medicaid exemption only lowers countable income. + if eligible[b]: + assert countable[a] <= countable[b] + TOLERANCE, c + assert supplement[a] >= supplement[b] - TOLERANCE, c + # 4. More non-exempt spousal earnings never lower countable income, + # for a client who stays one and has no in-kind support. + if not c["shelter_support"]: + if eligible[b] and eligible[c_]: + assert countable[c_] >= countable[b] - TOLERANCE, c + assert supplement[c_] <= supplement[b] + TOLERANCE, c + # 5. Roles. + assert countable[d] == pytest.approx(countable[b], abs=TOLERANCE), c + assert supplement[d] == pytest.approx(supplement[b], abs=TOLERANCE), c + + +def test_worked_example(): + """co_state_supplement YAML Case 29: the client has no income and the + spouse earns $1,500/month. With the spouse on Medicaid, SSI pays 783.50 + and 3.549(F) deems nothing: 994 - 783.50 = 210.50/month. Without + Medicaid, 3.549(C) deems (1,500 - 65) / 2 = 717.50 and the client is not + eligible.""" + case = { + "client_age": 40, + "client_pension": 0, + "client_interest": 0, + "client_earnings": 0, + "shelter_support": False, + "married": True, + "spouse_age": 42, + "spouse_earnings": 18_000, + "spouse_pension": 0, + "extra_spouse_earnings": 1_200, + } + results, _, _ = calculate([case]) + supplement = results["co_state_supplement"] + countable = results["co_state_supplement_countable_income"] + assert supplement["client_A0"] == pytest.approx(2_526, abs=TOLERANCE) + assert countable["client_A0"] == pytest.approx(0, abs=TOLERANCE) + assert supplement["client_B0"] == 0 + assert countable["client_B0"] == pytest.approx(8_610, abs=TOLERANCE) + assert_properties([case]) + + +def test_clients_who_leave_ssi(): + """Review counterexamples: a single client whose $1,025/month of + unearned income ends SSI, and a married client whose spouse's extra + earnings end SSI. Neither is then an AND-CS client, so neither has + countable income to compare.""" + single = { + "client_age": 40, + "client_pension": 12_000, + "client_interest": 300, + "client_earnings": 0, + "shelter_support": False, + "married": False, + "spouse_age": 40, + "spouse_earnings": 0, + "spouse_pension": 0, + "extra_spouse_earnings": 1, + } + married = { + **single, + "client_pension": 0, + "client_interest": 0, + "married": True, + "spouse_earnings": 30_000, + "extra_spouse_earnings": 12_000, + } + results, _, _ = calculate([single, married]) + assert results["ssi"]["client_A0"] == 0 + assert results["ssi"]["client_C1"] == 0 + assert results["ssi"]["client_B1"] > 0 + assert_properties([single, married]) + + +def test_ism_basis_switch_at_ssi_deeming_threshold(): + """Found by Hypothesis: a client with outside shelter support whose + spouse's income crosses SSI's spousal deeming threshold. SSA's presumed + maximum value drops from 994 / 3 + 20 = 351.33 to (1,491 / 3 + 20) / 2 = + 258.50, and 3.520.79(B)(6) charges the client that ISM. The spouse's + deemed earnings rise by (250.58 - 65) / 2 = 92.79, so countable income + falls by $0.04 a month: no breach of 3.549, which deems every dollar.""" + case = { + "client_age": 18, + "client_pension": 0, + "client_interest": 0, + "client_earnings": 0, + "shelter_support": True, + "married": True, + "spouse_age": 18, + "spouse_earnings": 1, + "spouse_pension": 2_958, + "extra_spouse_earnings": 3_006, + } + results, _, _ = calculate([case]) + countable = results["co_state_supplement_countable_income"] + assert countable["client_B0"] == pytest.approx(6_934, abs=1) + assert countable["client_C0"] < countable["client_B0"] + assert_properties([case]) + + +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 { + "client_age": draw(st.integers(min_value=18, max_value=59)), + "client_pension": draw(amount(12_000)), + "client_interest": draw(amount(300)), + "client_earnings": draw(amount(20_000)), + "shelter_support": draw(st.booleans()), + "married": draw(st.booleans()), + "spouse_age": draw(st.integers(min_value=18, max_value=59)), + "spouse_earnings": draw(amount(30_000)), + "spouse_pension": draw(amount(12_000)), + "extra_spouse_earnings": draw(st.integers(min_value=1, max_value=12_000)), + } + + @hypothesis.settings(max_examples=15, deadline=None, suppress_health_check=SLOW) + @hypothesis.given(st.lists(case(), min_size=1, max_size=8)) + def test_properties(cases): + assert_properties(cases) diff --git a/policyengine_us/tests/core/test_ssi_state_supplement_medicaid_dependency.py b/policyengine_us/tests/core/test_ssi_state_supplement_medicaid_dependency.py index a64c80a17a7..d11859aacde 100644 --- a/policyengine_us/tests/core/test_ssi_state_supplement_medicaid_dependency.py +++ b/policyengine_us/tests/core/test_ssi_state_supplement_medicaid_dependency.py @@ -36,7 +36,10 @@ def situation(year, state="IN"): @pytest.mark.parametrize("year", [2027, 2030, 2035]) -@pytest.mark.parametrize("state,benefit", [("IN", "in_ssp"), ("KS", "ks_sspp")]) +@pytest.mark.parametrize( + "state,benefit", + [("IN", "in_ssp"), ("KS", "ks_sspp"), ("CO", "co_state_supplement")], +) def test_ssp_snap_medicaid_calculation_order(year, state, benefit): """YAML cannot express repeated calculations in different orders.""" variables = (benefit, "snap", "medicaid_enrolled") @@ -54,7 +57,67 @@ def test_ssp_snap_medicaid_calculation_order(year, state, benefit): assert results[0]["snap"][1] > 0 # Kansas does not cover this childless adult through Medicaid expansion; # SNAP pass-through does not replace Medicaid's category requirement. - assert results[0]["medicaid_enrolled"].tolist() == [True, state == "IN"] + # Colorado AND-CS reads Medicaid enrollment for its deeming exemption + # (9 CCR 2503-5 3.549(F)). + assert results[0]["medicaid_enrolled"].tolist() == [True, state != "KS"] + + +def co_couple(year, snap_input): + """A disabled AND-CS client and a spouse with an $800/month pension whose + Medicaid community engagement exemption is SNAP receipt.""" + unit = {"members": ["client", "spouse"]} + spm_unit = dict(unit) + if snap_input: + spm_unit["snap"] = {year: 1_200} + return { + "people": { + "client": { + "age": {year: 45}, + "is_disabled": {year: True}, + "meets_ssi_disability_criteria": {year: True}, + }, + "spouse": { + "age": {year: 40}, + "pension_income": {year: 9_600}, + # Subject to SNAP's work requirement, so SNAP receipt passes + # through to Medicaid's community engagement requirement. + "is_snap_workfare_participant": {year: True}, + }, + }, + "tax_units": {"unit": unit}, + "marital_units": {"unit": unit}, + "families": {"unit": unit}, + "spm_units": {"unit": spm_unit}, + "households": {"unit": {**unit, "state_code": {year: "CO"}}}, + } + + +@pytest.mark.parametrize("snap_input", [False, True]) +def test_co_and_cs_spouse_medicaid_through_snap(snap_input): + """9 CCR 2503-5 3.549(F) deems nothing from a spouse enrolled in Medicaid + with income within the SSI limit. Here enrollment rests on SNAP receipt + (42 CFR 435.554(c)(7)), and SNAP counts AND-CS as income. The exemption, + and so the supplement, must not depend on which of AND-CS, SNAP and + Medicaid is calculated first.""" + year = 2030 + variables = ("co_state_supplement", "snap", "medicaid_enrolled") + results = [] + for order in (variables, variables[::-1]): + simulation = Simulation(situation=co_couple(year, snap_input)) + result = {name: simulation.calculate(name, year) for name in order} + result["exempt"] = simulation.calculate( + "co_state_supplement_deeming_exempt", f"{year}-01" + ) + results.append(result) + assert not any( + "co_state_supplement_medicaid" in name for name in simulation.branches + ) + for name in results[0]: + np.testing.assert_array_equal(results[0][name], results[1][name]) + assert results[0]["medicaid_enrolled"].tolist() == [True, True] + assert results[0]["exempt"].tolist() == [True, True] + # SSI deems the spouse's pension; AND-CS does not, so the client is paid. + assert results[0]["co_state_supplement"][0] > 0 @pytest.mark.parametrize( 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 dd50f2b0fc2..35c11d8a0ef 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 @@ -3,7 +3,7 @@ period: 2023 input: co_state_supplement_eligible: true - ssi_countable_income: 1_000 + co_state_supplement_countable_income: 1_000 ssi: 0 output: co_state_supplement: 9_968 @@ -13,7 +13,7 @@ period: 2023 input: co_state_supplement_eligible: true - ssi_countable_income: 0 + co_state_supplement_countable_income: 0 ssi: 2_000 output: co_state_supplement: 8_968 @@ -23,7 +23,7 @@ period: 2023 input: co_state_supplement_eligible: true - ssi_countable_income: 6_000 + co_state_supplement_countable_income: 6_000 ssi: 6_000 output: co_state_supplement: 0 @@ -33,7 +33,7 @@ period: 2023 input: co_state_supplement_eligible: true - ssi_countable_income: 0 + co_state_supplement_countable_income: 0 ssi: 10_968 output: co_state_supplement: 0 @@ -43,7 +43,7 @@ period: 2002 input: co_state_supplement_eligible: true - ssi_countable_income: 500 + co_state_supplement_countable_income: 500 ssi: 0 output: co_state_supplement: 6_484 @@ -53,7 +53,7 @@ period: 2005 input: co_state_supplement_eligible: true - ssi_countable_income: 0 + co_state_supplement_countable_income: 0 ssi: 6_000 output: co_state_supplement: 1_248 @@ -63,7 +63,7 @@ period: 2009 input: co_state_supplement_eligible: true - ssi_countable_income: 0 + co_state_supplement_countable_income: 0 ssi: 8_388 output: co_state_supplement: 0 @@ -73,7 +73,7 @@ period: 2012 input: co_state_supplement_eligible: true - ssi_countable_income: 1_000 + co_state_supplement_countable_income: 1_000 ssi: 0 output: co_state_supplement: 7_376 @@ -83,7 +83,7 @@ period: 2014 input: co_state_supplement_eligible: true - ssi_countable_income: 0 + co_state_supplement_countable_income: 0 ssi: 5_000 output: co_state_supplement: 3_652 @@ -93,7 +93,7 @@ period: 2023 input: co_state_supplement_eligible: false - ssi_countable_income: 0 + co_state_supplement_countable_income: 0 ssi: 0 output: co_state_supplement: 0 @@ -103,7 +103,7 @@ period: 2024 input: co_state_supplement_eligible: true - ssi_countable_income: 0 + co_state_supplement_countable_income: 0 ssi: 10_000 output: co_state_supplement: 1_316 @@ -113,7 +113,7 @@ period: 2025 input: co_state_supplement_eligible: true - ssi_countable_income: 2_000 + co_state_supplement_countable_income: 2_000 ssi: 5_000 output: co_state_supplement: 4_604 @@ -123,7 +123,7 @@ period: 2026 input: co_state_supplement_eligible: true - ssi_countable_income: 0 + co_state_supplement_countable_income: 0 ssi: 0 output: co_state_supplement: 11_928 @@ -336,3 +336,297 @@ ssi: [5_964, 5_964] co_state_supplement_countable_income: [2_982, 2_982] co_state_supplement: [2_982, 2_982] + +- name: Case 20, the 2026 payment floor raises a grant below $13. + # 9 CCR 2503-5 3.549(B): 994 - 985 = 9, raised to the $13 payment floor + # standard (3.546(E)) by a state supplementary payment. + period: 2026-01 + input: + co_state_supplement_eligible: true + co_state_supplement_countable_income: 0 + ssi: 985 + output: + co_state_supplement: 13 + +- name: Case 21, there was no payment floor in 2025. + # 967 - 958 = 9. Colorado met the pass-along requirement by total + # expenditures until the 2026 rule. + period: 2025-01 + input: + co_state_supplement_eligible: true + co_state_supplement_countable_income: 0 + ssi: 958 + output: + co_state_supplement: 9 + +- name: Case 22, a grant at the floor is unchanged. + period: 2026-01 + input: + co_state_supplement_eligible: true + co_state_supplement_countable_income: 0 + ssi: 981 + output: + co_state_supplement: 13 + +- name: Case 23, a grant of a fraction of a dollar is raised to the floor. + # 994 - 993.50 = 0.50: countable income is below the grant standard, so the + # client is eligible (3.520.781(A)) and the floor applies. + period: 2026-01 + input: + co_state_supplement_eligible: true + co_state_supplement_countable_income: 3.5 + ssi: 990 + output: + co_state_supplement: 13 + +- name: Case 24, a client whose countable income equals the grant standard gets no floor payment. + # 3.520.781(A): total countable income that "equals or exceeds" the grant + # standard makes the client ineligible, so no grant exists to raise. + period: 2026-01 + input: + co_state_supplement_eligible: true + co_state_supplement_countable_income: 200 + ssi: 794 + output: + co_state_supplement: 0 + +- name: Case 25, the floor applies to one member of an SSI couple. + # The head has a $487/month pension and the spouse has no income. Federal + # SSI: 487 - 20 = 467 couple countable income; couple payment + # 1,491 - 467 = 1,024, or 512 each (6,144/year). 3.549(A)(2): the head's own + # countable income is 487 - 10 = 477, so their grant is + # 994 - 512 - 477 = 5/month, raised to the $13 floor (156/year). The + # spouse's grant is 994 - 512 = 482/month (5,784/year). + period: 2026 + input: + people: + head: + age: 50 + is_disabled: true + meets_ssi_disability_criteria: true + pension_income: 5_844 + is_tax_unit_head: true + spouse: + age: 52 + is_disabled: true + meets_ssi_disability_criteria: 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: [6_144, 6_144] + co_state_supplement_countable_income: [5_724, 0] + co_state_supplement: [156, 5_784] + +- name: Case 26, a couple member whose countable income reaches the grant standard gets nothing. + # A $497/month pension: couple payment 1,491 - 477 = 1,014, or 507 each. + # The head's total countable income is 507 + 487 = 994, equal to the grant + # standard, so they are not eligible (3.520.781(A)) and get no floor + # payment. The spouse's grant is 994 - 507 = 487/month (5,844/year). + period: 2026 + input: + people: + head: + age: 50 + is_disabled: true + meets_ssi_disability_criteria: true + pension_income: 5_964 + is_tax_unit_head: true + spouse: + age: 52 + is_disabled: true + meets_ssi_disability_criteria: 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: [6_084, 6_084] + co_state_supplement: [0, 5_844] + +- name: Case 27, a single client with a disability benefit gets nothing. + # $300/month of SSDI: SSI 994 - 280 = 714. 3.549(A)(2) counts the same + # 280, so total countable income equals the $994 standard and the client is + # not eligible (3.520.781(A)); no floor payment is made. + period: 2026 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + social_security_disability: 3_600 + households: + household: + members: [person] + state_code: CO + output: + ssi: 8_568 + 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. + absolute_error_margin: 0.1 + period: 2026 + 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_952 + co_state_supplement: 3_976 + +- 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 + # and is enrolled in Medicaid. SSI deems (1,500 - 85) / 2 = 707.50 and pays + # 1,491 - 707.50 = 783.50/month (9,402/year). The spouse's SSI countable + # income, 707.50, is within the $994 individual rate, so 3.549(F) deems + # none of it to the client: grant 994 - 783.50 = 210.50/month + # (2,526/year). + period: 2026 + input: + people: + head: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + is_tax_unit_head: true + spouse: + age: 42 + employment_income: 18_000 + co_state_supplement_medicaid_enrolled: 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: [9_402, 0] + co_state_supplement_deemed_income: [0, 0] + co_state_supplement: [2_526, 0] + +- name: Case 30, a client whose working spouse is on no assistance program. + # Same couple as Case 29, but the spouse is not enrolled in Medicaid. + # 3.549(C) deems (1,500 - 65) / 2 = 717.50/month, so total countable income + # is 783.50 + 717.50 = 1,501, above the $994 standard. + period: 2026 + input: + people: + head: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + is_tax_unit_head: true + spouse: + age: 42 + employment_income: 18_000 + co_state_supplement_medicaid_enrolled: false + 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: [9_402, 0] + co_state_supplement_deemed_income: [8_610, 0] + co_state_supplement: [0, 0] + +- name: Case 31, in 2019 the members of an SSI couple deemed each other's pensions. + # The spouse has a $100/month pension. Federal SSI: couple countable income + # 100 - 20 = 80; couple payment 1,157 - 80 = 1,077, or 538.50 each. Before + # March 2020, 3.545(F) excluded only the SSI itself, so 3.545(C) deems the + # spouse's pension to the head, less the $20 the head (SSI only) did not + # use: 100 - 20 = 80. Head grant 771 - 538.50 - 80 = 152.50/month + # (1,830/year). The spouse counts 100 - 10 = 90 of their own and nothing + # from the head: 771 - 538.50 - 90 = 142.50/month (1,710/year). + period: 2019 + input: + people: + head: + age: 50 + is_disabled: true + meets_ssi_disability_criteria: true + is_tax_unit_head: true + spouse: + age: 52 + is_disabled: true + meets_ssi_disability_criteria: true + pension_income: 1_200 + 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: [6_462, 6_462] + co_state_supplement_deemed_income: [960, 0] + co_state_supplement: [1_830, 1_710] + +- name: Case 32, a single client whose income sits exactly at the standard gets no floor payment. + # A $7,174/year pension (597.83/month) and outside shelter support, counted + # at SSI's presumed maximum value of 351.33 (994 / 3 + 20). SSI counts + # 597.83 + 351.33 - 20 = 929.17 and pays 994 - 929.17 = 64.83. 3.549(A) + # counts the same 929.17, so total countable income is exactly the $994 + # standard and the client is not eligible (3.520.781(A)). Found by the + # property test: single precision left a grant of $0.00006, which the floor + # raised to $13. + period: 2026 + input: + people: + person: + age: 18 + is_disabled: true + meets_ssi_disability_criteria: true + pension_income: 7_174 + ssi_receives_outside_shelter_support: true + households: + household: + members: [person] + state_code: CO + output: + co_state_supplement: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.yaml index 774f71cc0cb..d2f3f1e6981 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.yaml @@ -1,10 +1,20 @@ -- name: Case 1, outside an SSI couple computation, countable income is SSI countable income. +- name: Case 1, a single client's countable income is their own income under 3.549(A). + # $300/month of SSDI less the $20 disregard: 280/month, or 3,360/year, with + # nothing deemed. period: 2026 input: - ssi_countable_income: 3_000 - state_code: CO + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + social_security_disability: 3_600 + households: + household: + members: [person] + state_code: CO output: - co_state_supplement_countable_income: 3_000 + co_state_supplement_countable_income: 3_360 - name: Case 2, spouses of a joint SSI claim each count their own income. # 9 CCR 2503-5 3.549(A)(2): own unearned income other than SSI, less the @@ -73,3 +83,33 @@ state_code: CO output: co_state_supplement_countable_income: [0, 810] + +- name: Case 4, a married client counts their own income plus income deemed from the spouse. + # 3.549(H): own 300 - 10 = 290 plus the spouse's pension 400 - 10 = 390 + # (see co_state_supplement_deemed_income Case 2): 680/month, or 8,160/year. + period: 2026 + input: + people: + head: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + social_security_disability: 3_600 + is_tax_unit_head: true + spouse: + age: 45 + pension_income: 4_800 + takes_up_medicaid_if_eligible: false + 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_state_supplement_countable_income: [8_160, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_deemed_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_deemed_income.yaml new file mode 100644 index 00000000000..cb8b0a17ddb --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_deemed_income.yaml @@ -0,0 +1,365 @@ +- name: Case 1, an ineligible spouse's earnings are deemed below SSI's deeming threshold. + # The client receives SSI only. The spouse is not disabled, earns + # $300/month and does not take up Medicaid. SSI deems nothing: 300 is under + # the $497 difference between the couple and individual rates (20 CFR + # 416.1163). 9 CCR 2503-5 3.549(C) has no threshold: + # (300 - 65) / 2 = 117.50/month, or 1,410/year. + period: 2026 + input: + people: + head: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + is_tax_unit_head: true + spouse: + age: 42 + employment_income: 3_600 + takes_up_medicaid_if_eligible: false + 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_income_deemed_from_ineligible_spouse: [0, 0] + ssi: [11_928, 0] + co_state_supplement_deeming_exempt: [true, false] + co_state_supplement_deemed_income: [1_410, 0] + +- name: Case 2, an ineligible spouse's unearned income is reduced by the rest of the couple's $20. + # The client has $300/month of SSDI and uses their $10 share of the $20 + # (3.549(A)(2)(c)(1)). The spouse's $400/month pension is reduced by the + # remaining $10 (3.549(D)(2)): 390/month, or 4,680/year. + period: 2026 + input: + people: + head: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + social_security_disability: 3_600 + is_tax_unit_head: true + spouse: + age: 45 + pension_income: 4_800 + takes_up_medicaid_if_eligible: false + 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_state_supplement_own_countable_income: [3_480, 0] + co_state_supplement_deemed_income: [4_680, 0] + co_state_supplement_countable_income: [8_160, 0] + +- name: Case 3, a spouse enrolled in Medicaid with income within the SSI limit has nothing deemed. + # Same couple as Case 2, but the spouse takes up Medicaid. Their SSI + # countable income, 400 - 20 = 380/month, is within the $994 individual + # rate, so 3.549(F) deems none of it. + period: 2026 + input: + people: + head: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + social_security_disability: 3_600 + is_tax_unit_head: true + spouse: + age: 45 + pension_income: 4_800 + 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_state_supplement_medicaid_enrolled: [true, true] + co_state_supplement_deeming_exempt: [true, true] + co_state_supplement_deemed_income: [0, 0] + +- name: Case 4, the members of an SSI couple deem nothing to each other. + # Both spouses receive SSI, so 3.549(F) deems neither's income to the + # other; each counts their own pension (see co_state_supplement Case 17). + period: 2026 + input: + people: + head: + age: 50 + is_disabled: true + meets_ssi_disability_criteria: true + pension_income: 4_800 + is_tax_unit_head: true + spouse: + age: 52 + is_disabled: true + meets_ssi_disability_criteria: true + pension_income: 2_400 + 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_state_supplement_deemed_income: [0, 0] + +- name: Case 5, parents' income is deemed to a disabled child using the spouse calculation. + # A disabled child aged 10 receives SSI. One parent earns $2,000/month, the + # other has no income, and neither takes up Medicaid. 3.549(E) deems the + # parents' income "using the same calculations as a spouse": + # (2,000 - 65) / 2 = 967.50/month, or 11,610/year. SSI's parental + # allocation (the $1,491 couple rate) leaves nothing to deem: + # (2,000 - 85) / 2 = 957.50 < 1,491. + period: 2026 + input: + people: + parent1: + age: 40 + employment_income: 24_000 + own_children_in_household: 1 + takes_up_medicaid_if_eligible: false + is_tax_unit_head: true + parent2: + age: 38 + own_children_in_household: 1 + takes_up_medicaid_if_eligible: false + is_tax_unit_spouse: true + child: + age: 10 + is_disabled: true + meets_ssi_disability_criteria: true + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent1, parent2, child] + marital_units: + parents: + members: [parent1, parent2] + child: + members: [child] + households: + household: + members: [parent1, parent2, child] + state_code: CO + output: + ssi_unearned_income_deemed_from_ineligible_parent: [0, 0, 0] + ssi: [0, 0, 11_928] + co_state_supplement_deemed_income: [0, 0, 11_610] + +- name: Case 6, a parent enrolled in Medicaid within the SSI limit has nothing deemed. + # Same family as Case 5, but the earning parent takes up Medicaid. Their SSI + # countable income, (2,000 - 85) / 2 = 957.50/month, is within the $994 + # individual rate, so 3.549(F) deems none of it. + period: 2026 + input: + people: + parent1: + age: 40 + employment_income: 24_000 + own_children_in_household: 1 + co_state_supplement_medicaid_enrolled: true + is_tax_unit_head: true + parent2: + age: 38 + own_children_in_household: 1 + takes_up_medicaid_if_eligible: false + is_tax_unit_spouse: true + child: + age: 10 + is_disabled: true + meets_ssi_disability_criteria: true + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent1, parent2, child] + marital_units: + parents: + members: [parent1, parent2] + child: + members: [child] + households: + household: + members: [parent1, parent2, child] + state_code: CO + output: + co_state_supplement_deeming_exempt: [true, false, true] + co_state_supplement_deemed_income: [0, 0, 0] + +- name: Case 7, a parent's unearned income is reduced by the child's unused $20. + # A disabled child aged 10 with no income other than SSI; the only parent + # has a $500/month pension and does not take up Medicaid. The child uses + # none of the $20 (3.549(A)(2)(c)(2)), so the parent's pension is reduced by + # the full $20 (3.549(D)(2)): 480/month, or 5,760/year. + period: 2026 + input: + people: + parent: + age: 40 + pension_income: 6_000 + own_children_in_household: 1 + takes_up_medicaid_if_eligible: false + is_tax_unit_head: true + child: + age: 10 + is_disabled: true + meets_ssi_disability_criteria: true + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child] + marital_units: + parent: + members: [parent] + child: + members: [child] + households: + household: + members: [parent, child] + state_code: CO + output: + co_state_supplement_deemed_income: [0, 5_760] + +- name: Case 8, the couple's combined $20 is not offered again after the client applies it to earnings. + # The married client reports SSI receipt with no payment this month and has + # $10 of interest and $200 of earnings. Their $10 share covers the interest + # (3.549(A)(2)(c)(1)); gross unearned income is under $20, so the other $10 + # reduces their earnings (3.549(A)(2)(f)): (200 - 65 - 10) / 2 = 62.50. + # That leaves none of the couple's combined $20 for the spouse's $50 + # pension (3.549(D)(2)), so all $50 is deemed. + period: 2026-01 + input: + people: + head: + age: 40 + is_ssi_disabled: true + receives_ssi: true + ssi: 0 + interest_income: 120 + employment_income: 2_400 + is_tax_unit_head: true + spouse: + age: 45 + pension_income: 600 + co_state_supplement_medicaid_enrolled: false + 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_state_supplement_own_countable_income: [62.5, 0] + co_state_supplement_deemed_income: [50, 0] + +- name: Case 9, a grandparent's income is not deemed to a grandchild. + # The infant's parent id names the mother, who has no income; the + # grandfather (named as the mother's parent) earns $865/month. 3.549(E) + # deems "the child's parents" income, so nothing is deemed: grant + # 994 - 500 = 494/month, or 5,928/year. + period: 2026 + input: + people: + grandfather: + age: 50 + person_id: 1 + employment_income: 10_380 + takes_up_medicaid_if_eligible: false + is_tax_unit_head: true + mother: + age: 20 + person_id: 2 + parent_1_id: 1 + takes_up_medicaid_if_eligible: false + is_tax_unit_dependent: true + infant: + age: 1 + person_id: 3 + parent_1_id: 2 + is_disabled: true + meets_ssi_disability_criteria: true + ssi: 6_000 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [grandfather, mother, infant] + marital_units: + grandfather: + members: [grandfather] + mother: + members: [mother] + infant: + members: [infant] + households: + household: + members: [grandfather, mother, infant] + state_code: CO + output: + co_state_supplement_deemed_income: [0, 0, 0] + co_state_supplement: [0, 0, 5_928] + +- name: Case 10, a parent under 18 has income deemed to their child. + # A 17-year-old earns $865/month and does not take up Medicaid; their + # disabled infant receives $500/month of SSI. 3.549(E)'s age limit applies + # to the client, not the parent: (865 - 65) / 2 = 400/month deemed, or + # 4,800/year; grant 994 - 500 - 400 = 94/month, or 1,128/year. + period: 2026 + input: + people: + parent: + age: 17 + employment_income: 10_380 + own_children_in_household: 1 + takes_up_medicaid_if_eligible: false + is_tax_unit_head: true + infant: + age: 1 + is_disabled: true + meets_ssi_disability_criteria: true + ssi: 6_000 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, infant] + marital_units: + parent: + members: [parent] + infant: + members: [infant] + households: + household: + members: [parent, infant] + state_code: CO + output: + co_state_supplement_deemed_income: [0, 4_800] + co_state_supplement: [0, 1_128] diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_deeming_exempt.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_deeming_exempt.yaml new file mode 100644 index 00000000000..217653629a3 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_deeming_exempt.yaml @@ -0,0 +1,92 @@ +- name: Case 1, an SSI recipient's income is not deemed. + # 9 CCR 2503-5 3.549(F). + period: 2026-01 + input: + ssi: 500 + state_code: CO + output: + co_state_supplement_deeming_exempt: true + +- name: Case 2, reported SSI receipt is enough. + period: 2026-01 + input: + ssi: 0 + receives_ssi: true + state_code: CO + output: + co_state_supplement_deeming_exempt: true + +- name: Case 3, a Medicaid enrollee with no income is exempt. + period: 2026-01 + input: + co_state_supplement_medicaid_enrolled: true + state_code: CO + output: + co_state_supplement_deeming_exempt: true + +- name: Case 4, an Old Age Pension recipient with no other income is exempt. + period: 2026-01 + input: + co_oap: 1_200 + state_code: CO + output: + co_state_supplement_deeming_exempt: true + +- name: Case 5, a Medicaid enrollee at the SSI limit is exempt. + # Earnings of $2,073/month: SSI countable income (2,073 - 20 - 65) / 2 = + # 994, no greater than the $994 individual benefit rate. + period: 2026-01 + input: + co_state_supplement_medicaid_enrolled: true + employment_income: 24_876 + state_code: CO + output: + co_state_supplement_deeming_exempt: true + +- name: Case 6, a Medicaid enrollee above the SSI limit is not exempt. + # Earnings of $2,074/month: (2,074 - 85) / 2 = 994.50 exceeds $994. + period: 2026-01 + input: + co_state_supplement_medicaid_enrolled: true + employment_income: 24_888 + state_code: CO + output: + co_state_supplement_deeming_exempt: false + +- name: Case 7, someone on none of the programs is not exempt. + period: 2026-01 + input: + co_state_supplement_medicaid_enrolled: false + co_oap: 0 + ssi: 0 + state_code: CO + output: + co_state_supplement_deeming_exempt: false + +- name: Case 8, before March 2020 a Medicaid enrollee's income was deemed. + # 3.545(F) (effective January 1, 2020) excluded only the income from those + # benefits; 3.549(F) took effect March 1, 2020. + period: 2020-02 + input: + co_state_supplement_medicaid_enrolled: true + state_code: CO + output: + co_state_supplement_deeming_exempt: false + +- name: Case 9, from March 2020 a Medicaid enrollee within the SSI limit is exempt. + period: 2020-03 + input: + co_state_supplement_medicaid_enrolled: true + state_code: CO + output: + co_state_supplement_deeming_exempt: true + +- name: Case 10, before March 2020 an SSI recipient's other income was deemed. + # 3.545(F) excluded only the SSI itself, which is not a deemed income + # source. + period: 2020-02 + input: + ssi: 500 + state_code: CO + output: + co_state_supplement_deeming_exempt: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_own_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_own_countable_income.yaml new file mode 100644 index 00000000000..f4add1af1c1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ssa/state_supplement/co_state_supplement_own_countable_income.yaml @@ -0,0 +1,162 @@ +- name: Case 1, a single client with a disability benefit. + # 9 CCR 2503-5 3.549(A)(2): $300/month of SSDI less the $20 disregard is + # 280/month, or 3,360/year. SSI counts the same 280. + period: 2026 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + social_security_disability: 3_600 + households: + household: + members: [person] + state_code: CO + output: + ssi_countable_income: 3_360 + co_state_supplement_own_countable_income: 3_360 + +- name: Case 2, a single SSI-only client with earnings gets no share of the unearned disregard. + # $500/month of earnings and no unearned income other than SSI. SSI applies + # the $20 general exclusion to earnings: (500 - 20 - 65) / 2 = 207.50. + # 3.549(A)(2)(c)(2) gives an SSI-only client no unearned disregard, and the + # client's gross unearned income, SSI included, is not under $20, so none + # reaches earnings (3.549(A)(2)(f)): (500 - 65) / 2 = 217.50/month, or + # 2,610/year. + period: 2026 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + employment_income: 6_000 + households: + household: + members: [person] + state_code: CO + output: + ssi_countable_income: 2_490 + co_state_supplement_own_countable_income: 2_610 + +- name: Case 3, the unused disregard does not reach earnings when SSI brings unearned income to $20. + # $5/month of interest and $200/month of earnings. The $20 disregard covers + # the $5 of interest (3.549(A)(2)(c)); gross unearned income including SSI + # is far above $20, so nothing reaches earnings (3.549(A)(2)(f)): + # (200 - 65) / 2 = 67.50/month, or 810/year. SSI moves the unused $15 to + # earnings: (200 - 65 - 15) / 2 = 60, or 720/year. + period: 2026 + input: + people: + person: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + interest_income: 60 + employment_income: 2_400 + households: + household: + members: [person] + state_code: CO + output: + ssi_countable_income: 720 + co_state_supplement_own_countable_income: 810 + +- name: Case 4, the unused disregard reaches earnings when gross unearned income is under $20. + # $10/month of interest, $200/month of earnings and no SSI payment. The $20 + # disregard covers the $10 of interest and the other $10 reduces earnings + # (3.549(A)(2)(f)): (200 - 65 - 10) / 2 = 62.50/month. Axiom's companion + # test client_income_disregards_and_grant gives the same 62.50. The client + # reports SSI receipt, so is an AND-CS client. + period: 2026-01 + input: + people: + person: + age: 40 + is_ssi_disabled: true + receives_ssi: true + interest_income: 120 + employment_income: 2_400 + ssi: 0 + households: + household: + members: [person] + state_code: CO + output: + co_state_supplement_own_countable_income: 62.5 + +- name: Case 5, a married client gets half of the $20 disregard. + # The client has $300/month of SSDI; the spouse is not disabled and has no + # income. 3.549(A)(2)(c)(1) splits the $20 between the spouses: + # 300 - 10 = 290/month, or 3,480/year. SSI, with no income to deem from the + # spouse, applies the whole $20: 280/month. + period: 2026 + input: + people: + head: + age: 40 + is_disabled: true + meets_ssi_disability_criteria: true + social_security_disability: 3_600 + is_tax_unit_head: true + spouse: + age: 42 + 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_countable_income: [3_360, 0] + co_state_supplement_own_countable_income: [3_480, 0] + +- name: Case 6, a single client receiving outside shelter support counts the presumed maximum value. + # In-kind support and maintenance is SSI's presumed maximum value, + # 994 / 3 + 20 = 351.33/month (20 CFR 416.1140), which 3.520.79(B)(6) + # charges to AND-CS as well. Less the $20 disregard: 331.33/month, or + # 3,976/year, as for SSI. + absolute_error_margin: 0.1 + period: 2026 + 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: + ssi_countable_income: 3_976 + co_state_supplement_own_countable_income: 3_976 + +- name: Case 7, a disabled student's earnings count in full. + # A disabled full-time student aged 19 earns $500/month. SSI excludes it + # under 20 CFR 416.1112(c)(3). 3.520.786 limits exempt income to the items + # it lists, which do not include that exclusion, so 3.549(A)(1) counts + # (500 - 65) / 2 = 217.50/month, or 2,610/year. + period: 2026 + input: + people: + person: + age: 19 + is_disabled: true + meets_ssi_disability_criteria: true + is_full_time_student: true + employment_income: 6_000 + households: + household: + members: [person] + state_code: CO + output: + ssi_countable_income: 0 + co_state_supplement_own_countable_income: 2_610 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 new file mode 100644 index 00000000000..7e7fd6bc2b7 --- /dev/null +++ b/policyengine_us/variables/gov/states/co/ssa/state_supplement/_and_cs_client_income.py @@ -0,0 +1,39 @@ +from policyengine_us.model_api import * + + +def and_cs_client_income(person, period, parameters): + """An AND-CS client's own monthly income and their use of the $20 disregard. + + 9 CCR 2503-5 3.549(A). Returns the client's gross earned income, gross + unearned income other than SSI, the part of the $20 unearned income + disregard applied to that unearned income, and the part of it that + 3.549(A)(2)(f) applies to earned income. + + Gross income uses SSI's earned and unearned income sources. 3.520.786 + limits exempt income to the items it lists, and SSI's student earned + income exclusion (20 CFR 416.1112(c)(3)) is not one of them, so it is + not deducted. + """ + 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) + 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 + # spouse. + married = person.marital_unit.nb_persons() == 2 + disregard = where(married, p.unearned / 2, p.unearned) + # 3.549(A)(2)(c)(2): a client whose only unearned income is SSI gets no + # unearned income disregard; min_ gives 0 when there is no other + # unearned income. + unearned_disregard = min_(disregard, unearned) + # 3.549(A)(2)(f): only when the client's gross unearned income, SSI + # included, is under $20 does the difference reduce earned income. + earned_disregard = where(unearned > 0, max_(0, p.unearned - unearned - ssi), 0) + return earned, unearned, unearned_disregard, earned_disregard diff --git a/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement.py b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement.py index dac180acbd4..beb6452a0f1 100644 --- a/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement.py +++ b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement.py @@ -5,13 +5,32 @@ class co_state_supplement(Variable): value_type = float entity = Person label = "Colorado State Supplement" + unit = USD definition_period = MONTH defined_for = "co_state_supplement_eligible" + reference = ( + # PDF pages 64, 91-96 + "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=64", + ) def formula(person, period, parameters): income = person("co_state_supplement_countable_income", period) ssi = person("ssi", period) total_countable_income = ssi + income p = parameters(period).gov.states.co.ssa.state_supplement - grant_standard = p.grant_standard - return max_(0, grant_standard - total_countable_income) + # 9 CCR 2503-5 3.548(B), 3.549(B): the grant is the standard less + # total countable income, SSI included. + grant = max_(0, p.grant_standard - total_countable_income) + # 3.520.781(A): a client whose total countable income equals or + # exceeds the grant standard is not eligible. Since 2012 the standard + # has equaled the SSI benefit standard for an individual, so this + # also excludes those "receiving the full SSI benefit standard" + # (3.546). A single client whose 3.549(A) income equals SSI's + # countable income has SSI plus that income exactly at the standard, + # so the test is on the grant in cents: single-precision arithmetic + # must not turn a $0 grant into a positive one. + eligible = np.round(grant, 2) > 0 + # 3.546(C)(2), (E), 3.549(B): an eligible client's grant below the + # payment floor standard is raised to it by a state supplementary + # payment. + return where(eligible, max_(grant, p.payment_floor), 0) diff --git a/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.py b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.py index 7c715bc5a79..c0574b902f2 100644 --- a/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.py +++ b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.py @@ -7,42 +7,12 @@ class co_state_supplement_countable_income(Variable): label = "Colorado State Supplement countable income other than SSI" unit = USD definition_period = MONTH - # PDF pages 93-94 + # PDF pages 93-96 reference = "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=93" - - def formula(person, period, parameters): - # 9 CCR 2503-5 3.549(A) counts each AND-CS client's own income. Where - # SSI's couple computation applies, ssi_countable_income instead gives - # each spouse half of the couple's pooled countable income. Both - # spouses then receive SSI, so 3.549(F) deems neither spouse's income - # to the other, and the client's own income is counted as follows. - couple_computation = person("ssi_couple_computation_applies", period) - p = parameters(period).gov.ssa.ssi.income.exclusions - earned = max_( - person("ssi_earned_income", period) - - person("ssi_blind_or_disabled_working_student_exclusion", period), - 0, - ) - # 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. - ism_share = person("ssi_in_kind_support_and_maintenance", period) / 2 - unearned = person("ssi_unearned_income", period) + ism_share - ssi = person("ssi", period) - # 3.549(A)(2)(c): $20 is deducted from unearned income other than SSI, - # split between married spouses; a client whose only unearned income - # is SSI gets no unearned income disregard. - unearned_disregard = min_(p.general / 2, unearned) - # 3.549(A)(2)(f): only when the client's gross unearned income, SSI - # included, is under $20 does the difference reduce earned income. - earned_disregard = where(unearned > 0, max_(0, p.general - unearned - ssi), 0) - # 3.549(A)(1): deduct $65 from gross earned income and halve the rest. - countable_earned = max_(0, earned - p.earned - earned_disregard) * ( - 1 - p.earned_share - ) - own_countable = unearned - unearned_disregard + countable_earned - return where( - couple_computation, - own_countable, - person("ssi_countable_income", period), - ) + # 9 CCR 2503-5 3.549(H): the client's own countable income plus income + # deemed from the spouse or parents. Sponsor deeming (3.549(G)) and the + # institutionalized spouse's MMMNA (3.549(F)) are not modeled. + adds = [ + "co_state_supplement_own_countable_income", + "co_state_supplement_deemed_income", + ] diff --git a/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_deemed_income.py b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_deemed_income.py new file mode 100644 index 00000000000..610f1da6d35 --- /dev/null +++ b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_deemed_income.py @@ -0,0 +1,91 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.co.ssa.state_supplement._and_cs_client_income import ( + and_cs_client_income, +) +from policyengine_us.variables.household.demographic.person._parent_links import ( + co_resident_parent_indices, + has_parent_ids, + unlinked_parent, +) + + +class co_state_supplement_deemed_income(Variable): + value_type = float + entity = Person + label = "Colorado State Supplement income deemed from a spouse or parents" + unit = USD + definition_period = MONTH + defined_for = "co_state_supplement_eligible" + # PDF pages 94-95 + reference = "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=94" + + def formula(person, period, parameters): + p = parameters(period).gov.states.co.ssa.state_supplement.income.disregards + # 3.549(D)(2): the spouse's unearned income is reduced by the part of + # the couple's combined $20 disregard the client did not use. + earned, _, unearned_disregard, earned_disregard = and_cs_client_income( + person, period, parameters + ) + used_on_earnings = min_(earned_disregard, max_(0, earned - p.earned)) + remaining_disregard = max_( + 0, p.unearned - unearned_disregard - used_on_earnings + ) + + # 3.549(F): no income is deemed from a spouse or parent who receives + # Adult Financial, SSI or Medicaid with income within the SSI limit. + deemable = ~person("co_state_supplement_deeming_exempt", period) + gross_earned = person("ssi_earned_income", period) * deemable + gross_unearned = person("ssi_unearned_income", period) * deemable + + def deem(earned_income, unearned_income): + # 3.549(C): deduct $65 from gross earned income and halve the rest. + # 3.549(D): deduct the remaining unearned income disregard. + # Unlike SSI (20 CFR 416.1163), there is no deeming threshold and + # no allocation for ineligible children. + return max_(0, earned_income - p.earned) * p.earned_share + max_( + 0, unearned_income - remaining_disregard + ) + + # 3.549(C)-(D): the client's spouse. + marital_unit = person.marital_unit + spouse_deemed = deem( + marital_unit.sum(gross_earned) - gross_earned, + marital_unit.sum(gross_unearned) - gross_unearned, + ) + + # 3.549(E): for an unemancipated child under 18, "the parents' income + # shall be deemed using the same calculations as a spouse". The + # parents' combined income is deemed in full to each child who is a + # client. The parents are those the child's parent ids name in the + # household, whatever their age; without ids, the tax unit's other + # members who are parents of a child no id names, as the parent-link + # contract requires (this cannot tell a grandparent from a parent). + year = period.this_year + first, second = co_resident_parent_indices(person, year) + + def from_named_parents(values): + return sum( + np.where(rows >= 0, values[np.maximum(rows, 0)], 0) + for rows in (first, second) + ) + + parent = unlinked_parent(person, year) + tax_unit = person.tax_unit + + def from_tax_unit_parents(values): + return tax_unit.sum(values * parent) - values * parent + + linked = has_parent_ids(person, year) + parents_deemed = person("is_child", year) * deem( + where( + linked, + from_named_parents(gross_earned), + from_tax_unit_parents(gross_earned), + ), + where( + linked, + from_named_parents(gross_unearned), + from_tax_unit_parents(gross_unearned), + ), + ) + return spouse_deemed + parents_deemed diff --git a/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_deeming_exempt.py b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_deeming_exempt.py new file mode 100644 index 00000000000..42d16dde285 --- /dev/null +++ b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_deeming_exempt.py @@ -0,0 +1,47 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.ssa.ssi.eligibility.income._apply_ssi_exclusions import ( + _apply_ssi_exclusions, +) + + +class co_state_supplement_deeming_exempt(Variable): + value_type = bool + entity = Person + label = "Colorado State Supplement spouse or parent income not deemed" + definition_period = MONTH + # PDF page 95 + reference = "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=95" + + def formula(person, period, parameters): + p = parameters(period).gov.states.co.ssa.state_supplement.income.deeming + if not p.assistance_exemption_applies: + # Before March 2020, 3.545(F) excluded only "the income from those + # benefits" (public assistance, SSI or medical assistance), which + # the deemed income sources do not include. The recipient's other + # income was deemed. + return np.zeros(person.count, dtype=bool) + # 9 CCR 2503-5 3.549(F): a spouse or parent who receives Adult + # Financial grant payments, SSI or Medicaid "and has income no greater + # than the SSI limit" has no income deemed to the AND-CS client. + # SSA has found an SSI recipient's income within the SSI limit (for a + # member of a couple, the couple's), so SSI receipt alone qualifies. + # Each spouse of an SSI couple is then a client on their own income, + # as 3.548(A) requires. + receives_ssi = (person("ssi", period) > 0) | person("receives_ssi", period) + # Old Age Pension is the Adult Financial program modeled for someone + # who is not on SSI; AND-CS requires SSI. + year = period.this_year + adult_financial = person("co_oap", year) > 0 + medicaid = person("co_state_supplement_medicaid_enrolled", year) + # The rule does not define the SSI limit. We read it as SSI's income + # test for an individual: countable income, after SSI's exclusions, + # no greater than the individual federal benefit rate. + countable = _apply_ssi_exclusions( + person("ssi_earned_income", year), + person("ssi_unearned_income", year), + parameters, + year, + ) + fbr = parameters(period).gov.ssa.ssi.amount.individual + within_ssi_limit = countable <= fbr * MONTHS_IN_YEAR + return receives_ssi | ((adult_financial | medicaid) & within_ssi_limit) diff --git a/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_medicaid_enrolled.py b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_medicaid_enrolled.py new file mode 100644 index 00000000000..0a7952db0f1 --- /dev/null +++ b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_medicaid_enrolled.py @@ -0,0 +1,48 @@ +from policyengine_us.model_api import * + + +class co_state_supplement_medicaid_enrolled(Variable): + value_type = bool + entity = Person + label = "Medicaid enrollment for the Colorado State Supplement deeming exemption" + definition_period = YEAR + # PDF page 95 + reference = "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=95" + documentation = ( + "Medicaid enrollment of an AND-CS client's spouse or parent, for the " + "9 CCR 2503-5 3.549(F) deeming exemption. Under the Medicaid community " + "engagement requirement, enrollment can turn on membership of a " + "household receiving SNAP (42 CFR 435.554(c)(7)), and SNAP counts " + "AND-CS as income. Enrollment is therefore computed in a private " + "branch where this variable is false for everyone, so AND-CS deems " + "every Medicaid spouse's or parent's income and is no higher than " + "its final value, and SNAP is no lower." + ) + + def formula(person, period, parameters): + p = parameters(period).gov.hhs.medicaid.eligibility.work_requirements + if not p.applies: + return person("medicaid_enrolled", period) + # The branch keeps the SNAP route to enrollment, and AND-CS inside it + # reads this variable as an input, so nothing recurses. The parent + # cannot hold this variable before computing it, so the branch + # always shares the parent's cache, and the result does not depend + # on whether SNAP, Medicaid or AND-CS is calculated first. + simulation = person.simulation + branch_name = f"{simulation.branch_name}_co_state_supplement_medicaid_{period}" + try: + branch = get_override_branch( + simulation, + branch_name, + period, + { + "co_state_supplement_medicaid_enrolled": np.zeros( + person.count, dtype=bool + ) + }, + ) + return branch.calculate("medicaid_enrolled", period) + finally: + # A branch clones cached arrays; retaining it would duplicate a + # substantial part of a full-population simulation in memory. + simulation.branches.pop(branch_name, None) diff --git a/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_own_countable_income.py b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_own_countable_income.py new file mode 100644 index 00000000000..f35360bbdd0 --- /dev/null +++ b/policyengine_us/variables/gov/states/co/ssa/state_supplement/co_state_supplement_own_countable_income.py @@ -0,0 +1,29 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.co.ssa.state_supplement._and_cs_client_income import ( + and_cs_client_income, +) + + +class co_state_supplement_own_countable_income(Variable): + value_type = float + entity = Person + label = "Colorado State Supplement countable income of the client other than SSI" + unit = USD + definition_period = MONTH + defined_for = "co_state_supplement_eligible" + # PDF pages 93-94 + reference = "https://www.sos.state.co.us/CCR/GenerateRulePdf.do?fileName=9+CCR+2503-5&ruleVersionId=12428#page=93" + + def formula(person, period, parameters): + # 9 CCR 2503-5 3.549(A) counts each AND-CS client's own income, + # whether they are single, married or a member of an SSI couple. + # The SSI that 3.549(A)(2)(d) adds back is added in co_state_supplement. + p = parameters(period).gov.states.co.ssa.state_supplement.income.disregards + earned, unearned, unearned_disregard, earned_disregard = and_cs_client_income( + person, period, parameters + ) + # 3.549(A)(1): deduct $65 from gross earned income and halve the rest. + countable_earned = ( + max_(0, earned - p.earned - earned_disregard) * p.earned_share + ) + return unearned - unearned_disregard + countable_earned