From 6dab1c798e5313c032bb79b54c90db4cb8e802b6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 9 Oct 2026 14:39:48 -0400 Subject: [PATCH 01/17] Remove the BBCE net income test from California MCE CalFresh Modified Categorical Eligibility has no net income test. CDSS ACL 12-62 (examples 4-5), ACL 13-32, ACL 14-56 and ACL 15-42 direct that an MCE household of three or more gets the issuance-table allotment even when net income exceeds the net standard, and that one of one or two gets the minimum allotment. ACL 13-32 applies this to elderly or disabled households at or below 200% of poverty. MPP 63-409.1 and 7 CFR 273.9(a) exempt categorically eligible households from the income standards. Set CA to false in both net_applies parameters, cite the CDSS sources, move the net-standard rounding cases to Rhode Island, and add California cases with FY 2026 values from CDSS ACIN I-46-25. Co-Authored-By: Claude Opus 5.5 --- .../fix-ca-mce-net-income-test.fixed.md | 1 + .../income_limit/net_applies/hheod.yaml | 19 ++- .../income_limit/net_applies/non_hheod.yaml | 21 ++- .../meets_tanf_non_cash_net_income_test.yaml | 87 +++++++++- .../snap/eligibility/is_snap_eligible.yaml | 153 ++++++++++++++++++ 5 files changed, 272 insertions(+), 9 deletions(-) create mode 100644 changelog.d/fix-ca-mce-net-income-test.fixed.md diff --git a/changelog.d/fix-ca-mce-net-income-test.fixed.md b/changelog.d/fix-ca-mce-net-income-test.fixed.md new file mode 100644 index 00000000000..e13016530b1 --- /dev/null +++ b/changelog.d/fix-ca-mce-net-income-test.fixed.md @@ -0,0 +1 @@ +Remove the SNAP net income test from California's broad-based categorical eligibility (CalFresh Modified Categorical Eligibility) for households with and without elderly or disabled members, per CDSS ACLs 12-62, 13-32, 14-56 and 15-42. diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml index 85355ff472d..c94404c80d1 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml @@ -5,7 +5,12 @@ AL: AZ: 2015-10-01: false CA: - 2015-10-01: true + # Elderly or disabled households with gross income at or below 200% of the + # poverty line are conferred MCE like any other household, and MCE has no + # net income test (CDSS ACL 13-32 note; ACL 15-42). Those above 200% are + # not MCE and face the regular net income and resource tests instead, + # which gross_hheod.yaml routes them to. + 2015-10-01: false CO: 2015-10-01: true 2024-07-01: false @@ -104,5 +109,17 @@ metadata: href: https://www.snapscreener.com/?p=table - title: USDA ERS SNAP Policy Database (bbce_elddisinclmt column, Jan 1996 - Dec 2020) href: https://www.ers.usda.gov/data-products/snap-policy-data-sets + - title: CDSS All County Letter 13-32, MCE/BBCE and elderly/disabled CalFresh households + href: https://www.cdss.ca.gov/lettersnotices/EntRes/getinfo/acl/2013/13-32.pdf#page=3 + - title: "CDSS All County Letter 14-56, CalFresh Modified Categorical Eligibility #page 2,4" + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2014/14-56.pdf#page=2 + - title: CDSS All County Letter 14-63, CalFresh households eligible but entitled to no benefits + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2014/14-63.pdf#page=2 + - title: CDSS All County Letter 15-42, MCE gross income limit and income reporting thresholds + href: https://www.cdss.ca.gov/lettersnotices/EntRes/getinfo/acl/2015/15-42.pdf#page=4 + - title: CDSS Manual of Policies and Procedures 63-409.1, requirements for participation + href: https://www.cdss.ca.gov/ord/entres/getinfo/pdf/fsman04b.pdf#page=34 + - title: 7 CFR 273.9(a), income eligibility standards + href: https://www.law.cornell.edu/cfr/text/7/273.9#a breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml index bd3e7e648b3..0170a7eeeee 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml @@ -5,7 +5,14 @@ AL: AZ: 2015-10-01: false CA: - 2015-10-01: true + # CalFresh Modified Categorical Eligibility (MCE) has no net income test. + # An MCE household of three or more receives the allotment the issuance + # table gives for its net income, even above the net standard, and a + # household of one or two receives the minimum allotment (CDSS ACL 12-62, + # examples 4-5; ACL 14-56; ACL 15-42). Zero-allotment households of three + # or more are denied for that reason, not for failing a net test + # (ACL 14-63). + 2015-10-01: false CO: 2015-10-01: true 2024-07-01: false @@ -102,5 +109,17 @@ metadata: href: https://www.snapscreener.com/?p=table - title: USDA ERS SNAP Policy Database (bbce_inclmt column, Jan 1996 - Dec 2020) href: https://www.ers.usda.gov/data-products/snap-policy-data-sets + - title: "CDSS All County Letter 12-62, examples of MCE/BBCE households for CalFresh #page 4-5" + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2012/12-62.pdf#page=4 + - title: CDSS All County Letter 14-56, CalFresh Modified Categorical Eligibility + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2014/14-56.pdf#page=4 + - title: CDSS All County Letter 14-63, CalFresh households eligible but entitled to no benefits + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2014/14-63.pdf#page=2 + - title: CDSS All County Letter 15-42, MCE gross income limit and income reporting thresholds + href: https://www.cdss.ca.gov/lettersnotices/EntRes/getinfo/acl/2015/15-42.pdf#page=4 + - title: CDSS Manual of Policies and Procedures 63-409.1, requirements for participation + href: https://www.cdss.ca.gov/ord/entres/getinfo/pdf/fsman04b.pdf#page=34 + - title: 7 CFR 273.9(a), income eligibility standards + href: https://www.law.cornell.edu/cfr/text/7/273.9#a breakdown: - state_code diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_net_income_test.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_net_income_test.yaml index 8b0d0459554..aa355e00676 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_net_income_test.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_net_income_test.yaml @@ -1,30 +1,37 @@ -- name: Default household meets California's BBCE net income test (100%). +# California's Modified Categorical Eligibility (MCE) has no net income +# test (CDSS ACL 12-62, ACL 13-32, ACL 14-56, ACL 15-42), so the cases on the +# mechanics of the net standard use Rhode Island, whose BBCE keeps the net +# test for both household types. +- name: Default California household meets the BBCE net income test because MCE has none. period: 2022 output: meets_tanf_non_cash_net_income_test: true -- name: California household with net income exactly at the standard meets the net income test. +- name: Rhode Island household with net income exactly at the standard meets the net income test. period: 2022 input: snap_fpg: 12_000 snap_net_income: 12_000 + state_code: RI output: # Monthly standard is ceil(1.0 x 1,000) = 1,000; net income of 1,000 # per month is exactly at it. meets_tanf_non_cash_net_income_test: true -- name: California household with net income above the standard fails the net income test. +- name: Rhode Island household with net income above the standard fails the net income test. period: 2022 input: snap_fpg: 12_000 snap_net_income: 12_120 + state_code: RI output: # Net income of 1,010 per month exceeds the 1,000 standard. meets_tanf_non_cash_net_income_test: false -- name: California household at the rounded-up whole-dollar standard meets the net income test. +- name: Rhode Island household at the rounded-up whole-dollar standard meets the net income test. period: 2022 input: + state_code: RI # Monthly poverty guideline of 2,220.83 yields a whole-dollar standard # of ceil(2,220.83) = 2,221, so a rounded net income of exactly 2,221 # still passes; a raw ratio comparison (2,221 / 2,220.83 > 1) would @@ -34,7 +41,7 @@ output: meets_tanf_non_cash_net_income_test: true -- name: California nine-person household at the published FY 2026 net standard meets the net income test. +- name: Rhode Island nine-person household at the published FY 2026 net standard meets the net income test. period: 2026-01 input: people: @@ -50,11 +57,11 @@ households: household: members: [person1] - state_code: CA + state_code: RI output: meets_tanf_non_cash_net_income_test: true -- name: California nine-person household just above the FY 2026 net standard fails the net income test. +- name: Rhode Island nine-person household just above the FY 2026 net standard fails the net income test. period: 2026-01 input: people: @@ -70,9 +77,75 @@ # $4,972 under 7 CFR 273.9(a)(3)(ii). Net income of $4,973 is $1 # above it. snap_net_income: 4_973 + households: + household: + members: [person1] + state_code: RI + output: + meets_tanf_non_cash_net_income_test: false + +- name: California MCE household of four above the FY 2026 net standard meets the net income test. + period: 2026-01 + input: + people: + person1: {} + spm_units: + spm_unit: + members: [person1] + snap_unit_size: 4 + is_tanf_non_cash_hheod: false + # $286 above the four-person FY 2026 net standard of $2,680 (CDSS + # ACIN I-46-25, attachment I). + snap_net_income: 2_966 households: household: members: [person1] state_code: CA output: + # An MCE household of three or more gets the issuance-table allotment + # "even if the household's net income exceeds the maximum amount + # allowable" (CDSS ACL 12-62, example 5; ACL 14-56; ACL 15-42). + meets_tanf_non_cash_net_income_test: true + +- name: California MCE elderly or disabled household above the FY 2026 net standard meets the net income test. + period: 2026-01 + input: + people: + person1: {} + spm_units: + spm_unit: + members: [person1] + snap_unit_size: 1 + is_tanf_non_cash_hheod: true + # $486 above the one-person FY 2026 net standard of $1,305 (CDSS + # ACIN I-46-25, attachment I). + snap_net_income: 1_791 + households: + household: + members: [person1] + state_code: CA + output: + # Elderly or disabled households at or below 200% of the poverty line + # are conferred MCE, and MCE households of one or two get the minimum + # allotment even above the net standard (CDSS ACL 13-32, note). + meets_tanf_non_cash_net_income_test: true + +- name: Rhode Island household of four above the FY 2026 net standard fails the net income test. + period: 2026-01 + input: + people: + person1: {} + spm_units: + spm_unit: + members: [person1] + snap_unit_size: 4 + is_tanf_non_cash_hheod: false + snap_net_income: 2_966 + households: + household: + members: [person1] + state_code: RI + output: + # The same household fails where the state's BBCE keeps the net test: + # $2,966 exceeds the four-person FY 2026 standard of $2,680. meets_tanf_non_cash_net_income_test: false diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml index ae8049266a2..b0806cf9abf 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml @@ -223,3 +223,156 @@ snap_unit_size: 0 is_snap_eligible: false snap: 0 + +# California Modified Categorical Eligibility (MCE) in FY 2026. CDSS ACIN +# I-46-25, attachment I: net standard (100% of poverty) $1,305 for one person +# and $2,680 for four; MCE gross standard (200%) $2,610 and $5,360; regular +# gross standard (130%) $3,483 for four; maximum allotment $298 and $994; +# standard deduction $209 (1-3 people) and $223 (4); minimum allotment $24. +# No housing costs, so the excess shelter deduction is zero at these incomes +# whichever utility allowance applies. +- name: California MCE household of four above the net standard is eligible for the issuance-table allotment. + period: 2026-01 + absolute_error_margin: 0.1 + input: + people: + parent1: + age: 35 + employment_income: 48_000 + weekly_hours_worked_before_lsr: 40 + parent2: + age: 33 + child1: + age: 8 + child2: + age: 4 + spm_units: + spm_unit: + members: [parent1, parent2, child1, child2] + housing_cost: 0 + tax_units: + tax_unit: + members: [parent1, parent2, child1, child2] + households: + household: + members: [parent1, parent2, child1, child2] + state_code: CA + output: + # Gross $4,000 is above the regular $3,483 but within the $5,360 MCE + # standard, so MCE is conferred. + meets_snap_gross_income_test: false + is_tanf_non_cash_eligible: true + # Net $4,000 - 20% earned income deduction ($800) - $223 = $2,977, above + # the $2,680 net standard. + snap_net_income: 2_977 + meets_snap_net_income_test: false + # MCE households of three or more get the issuance-table allotment "even + # if the household's net income exceeds the maximum amount allowable" + # (CDSS ACL 12-62, example 5; ACL 14-56; ACL 15-42). + meets_snap_categorical_eligibility: true + is_snap_eligible: true + # $994 - ceil(30% x $2,977 = $893.10) = $994 - $894. + snap: 100 + +- name: California MCE elderly person above the net standard is eligible for the minimum allotment. + period: 2026-01 + absolute_error_margin: 0.1 + input: + people: + person: + age: 65 + social_security: 24_000 + # The 2026 standard Medicare Part B premium, $202.90 a month. + health_insurance_premiums: 2_434.8 + spm_units: + spm_unit: + members: [person] + housing_cost: 0 + tax_units: + tax_unit: + members: [person] + households: + household: + members: [person] + state_code: CA + output: + # Gross $2,000 is within the $2,610 MCE standard, and elderly or + # disabled households at or below 200% of poverty are conferred MCE + # (CDSS ACL 13-32). + is_tanf_non_cash_eligible: true + # Net $2,000 - $209 - ($202.90 - $35 excess medical) = $1,623.10, + # rounded to $1,623, above the $1,305 net standard. + snap_net_income: 1_623 + meets_snap_net_income_test: false + # MCE households of one or two get the minimum allotment even above the + # net standard (CDSS ACL 13-32, note; ACL 12-62, example 4). + is_snap_eligible: true + # $298 - ceil(30% x $1,623 = $486.90) < 0, raised to the $24 minimum. + snap: 24 + +- name: California elderly person above 200% of poverty is not MCE and fails the net standard. + period: 2026-01 + absolute_error_margin: 0.1 + input: + people: + person: + age: 65 + social_security: 32_400 + # The 2026 standard Medicare Part B premium, $202.90 a month. + health_insurance_premiums: 2_434.8 + spm_units: + spm_unit: + members: [person] + housing_cost: 0 + tax_units: + tax_unit: + members: [person] + households: + household: + members: [person] + state_code: CA + output: + # Gross $2,700 exceeds the $2,610 MCE standard, so MCE is not conferred + # and the household must be net income eligible (CDSS ACL 13-32, + # example 2): net $2,700 - $209 - $167.90 = $2,323.10, rounded to + # $2,323, exceeds $1,305. + is_tanf_non_cash_eligible: false + snap_net_income: 2_323 + meets_snap_net_income_test: false + is_snap_eligible: false + snap: 0 + +- name: California household of four above 200% of poverty is not MCE and fails the gross standard. + period: 2026-01 + absolute_error_margin: 0.1 + input: + people: + parent1: + age: 35 + employment_income: 66_000 + weekly_hours_worked_before_lsr: 40 + parent2: + age: 33 + child1: + age: 8 + child2: + age: 4 + spm_units: + spm_unit: + members: [parent1, parent2, child1, child2] + housing_cost: 0 + tax_units: + tax_unit: + members: [parent1, parent2, child1, child2] + households: + household: + members: [parent1, parent2, child1, child2] + state_code: CA + output: + # Gross $5,500 exceeds the $5,360 MCE standard, so MCE is not conferred + # and the $3,483 regular gross standard applies (CDSS ACL 14-56, + # example 2). + is_tanf_non_cash_eligible: false + meets_snap_gross_income_test: false + is_snap_eligible: false + snap: 0 From 14ccbd2b132036aa165f24254427e4d8b1c8ce2f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 9 Oct 2026 21:08:20 -0400 Subject: [PATCH 02/17] Update a CARE test for the SNAP minimum allotment it now includes CARE countable income counts SNAP. The Case 8 household is an MCE household above the net standard in November and December 2024, so with the BBCE net test gone it receives the $23 FY 2025 minimum allotment in those two months (CDSS ACL 12-62, example 4), and its countable income is 30,046, not 30,000. Co-Authored-By: Claude Opus 5.5 --- .../policy/baseline/gov/states/ca/cpuc/integration.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml index 105d8987278..e09440e9c07 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml @@ -190,7 +190,12 @@ ca_care_categorically_eligible: false tenant_pays_utilities: false output: - ca_cpuc_countable_income: 30_000 + # Countable income includes SNAP. With no work hours entered, this adult + # fails the SNAP ABAWD work requirement through October 2024. In November + # and December, as an MCE household above the net standard, the household + # receives the $23 FY 2025 minimum allotment (CDSS ACL 12-62, example 4): + # 30,000 + 2 x 23. + ca_cpuc_countable_income: 30_046 ca_care_income_eligible: true # The model's CARE gate treats a household with utilities included in # rent as ineligible, so neither the electricity nor the gas discount is From b99a494bcc4959f50d9284e520321827053741f5 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 13:26:11 -0400 Subject: [PATCH 03/17] Exclude SNAP from California CARE and FERA countable income --- .../hub-ca-care-excludes-snap-1010.fixed.md | 1 + .../gov/states/ca/cpuc/income_sources.yaml | 14 ++++++-- .../ca/cpuc/ca_cpuc_countable_income.yaml | 32 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 changelog.d/hub-ca-care-excludes-snap-1010.fixed.md diff --git a/changelog.d/hub-ca-care-excludes-snap-1010.fixed.md b/changelog.d/hub-ca-care-excludes-snap-1010.fixed.md new file mode 100644 index 00000000000..084d1437bde --- /dev/null +++ b/changelog.d/hub-ca-care-excludes-snap-1010.fixed.md @@ -0,0 +1 @@ +Exclude SNAP benefits from California CARE and FERA countable income in all modeled years. diff --git a/policyengine_us/parameters/gov/states/ca/cpuc/income_sources.yaml b/policyengine_us/parameters/gov/states/ca/cpuc/income_sources.yaml index 066d595d891..3a8a29b4b35 100644 --- a/policyengine_us/parameters/gov/states/ca/cpuc/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ca/cpuc/income_sources.yaml @@ -11,7 +11,13 @@ description: >- # employment-related, non-cash income. The same footnote appears on the # CPUC-filed tariff sheet referenced below. # -# Housing subsidies are the one exception. D.14-08-030, issued August 14, +# SNAP benefits are excluded in both dated lists: 7 U.S.C. 2017(b) prohibits +# treating their value as income or resources under any federal, state or +# local law, including California's CARE and FERA programs. +# The same exclusion appears in the 2010 statute, before the housing +# decision; this corrects all modeled years rather than adding a new date. +# +# Separately, D.14-08-030, issued August 14, # 2014, holds in Section 6.2 that the income definition remains unchanged # except that housing subsidies will not be considered income, and Ordering # Paragraph 40(3) adopts "Housing subsidies are not counted as income". The @@ -67,7 +73,6 @@ values: # Public assistance payments - tanf - ssi - - snap - wic # Social Security - social_security @@ -110,7 +115,6 @@ values: # Public assistance payments - tanf - ssi - - snap - wic # Social Security - social_security @@ -137,6 +141,10 @@ metadata: period: year label: CA CPUC CARE/FERA income sources reference: + - title: 7 U.S.C. 2017(b), exclusion of SNAP benefits from income and resources + href: https://www.govinfo.gov/content/pkg/USCODE-2024-title7/html/USCODE-2024-title7-chap51-sec2017.htm + - title: 7 U.S.C. 2017(b), 2010 edition + href: https://www.govinfo.gov/content/pkg/USCODE-2010-title7/pdf/USCODE-2010-title7-chap51-sec2017.pdf#page=1 - title: PG&E Form 01-9077, CARE/FERA Program Application (Rev. 6.26) href: https://www.pge.com/assets/pge/localized/en/docs/account/billing-and-assistance/care-fera-application.pdf - title: >- diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/ca_cpuc_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/ca_cpuc_countable_income.yaml index b95a58c4a11..f5786bf4c18 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/ca_cpuc_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/ca_cpuc_countable_income.yaml @@ -112,3 +112,35 @@ spm_unit_capped_housing_subsidy: 25 output: ca_cpuc_countable_income: 1_000 + +- name: SNAP is excluded before the housing assistance exclusion. + period: 2013 + input: + age: 40 + state_code: CA + employment_income: 1_000 + housing_assistance: 500 + snap: 1_200 + tanf: 0 + ssi: 0 + wic: 0 + output: + # $1,000 wages + $500 housing; SNAP is excluded under 7 U.S.C. 2017(b). + # https://www.govinfo.gov/content/pkg/USCODE-2024-title7/html/USCODE-2024-title7-chap51-sec2017.htm + ca_cpuc_countable_income: 1_500 + +- name: SNAP cannot push income at the CARE limit above income eligibility. + period: 2026 + input: + age: 40 + state_code: CA + employment_income: 43_280 + snap: 1_200 + ca_care_categorically_eligible: false + output: + # CARE uses the two-person poverty line for a one-person household: + # 200% * ($15,960 + $5,680) = $43,280. SNAP adds $0 to countable income + # under 7 U.S.C. 2017(b), so income remains exactly at the limit. + # https://www.govinfo.gov/content/pkg/USCODE-2024-title7/html/USCODE-2024-title7-chap51-sec2017.htm + ca_cpuc_countable_income: 43_280 + ca_care_income_eligible: true From 2e5f00e7dffef15943b98e450f433ae6392cc594 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 13:40:45 -0400 Subject: [PATCH 04/17] Reconcile California MCE regressions and CARE income with SNAP exclusion --- .../income_limit/net_applies/hheod.yaml | 10 +++ .../income_limit/net_applies/non_hheod.yaml | 10 +++ .../gov/states/ca/cpuc/integration.yaml | 10 ++- .../snap/eligibility/is_snap_eligible.yaml | 61 ++++++++++++++++++- .../snap/snap_child_support_integration.yaml | 14 +++++ 5 files changed, 96 insertions(+), 9 deletions(-) diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml index c94404c80d1..785e338ef80 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml @@ -10,6 +10,10 @@ CA: # net income test (CDSS ACL 13-32 note; ACL 15-42). Those above 200% are # not MCE and face the regular net income and resource tests instead, # which gross_hheod.yaml routes them to. + # ACL 11-11 expanded MCE to all non-assistance CalFresh households on + # 2011-02-01. Retain 2015-10-01 as this model's BBCE baseline, matching + # the surrounding California gross, asset, and poverty-year parameters; + # this flag alone does not establish modeled eligibility in 2011. 2015-10-01: false CO: 2015-10-01: true @@ -119,6 +123,12 @@ metadata: href: https://www.cdss.ca.gov/lettersnotices/EntRes/getinfo/acl/2015/15-42.pdf#page=4 - title: CDSS Manual of Policies and Procedures 63-409.1, requirements for participation href: https://www.cdss.ca.gov/ord/entres/getinfo/pdf/fsman04b.pdf#page=34 + - title: CDSS June 30, 2014 letter, CalFresh Modified Categorical Eligibility + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/coletters/calfreshmodifiedcategoricaleligibility.pdf#page=3 + - title: CDSS All County Letter 11-11, statewide expansion of MCE to all NACF households + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2011/11-11.pdf + - title: 7 CFR 273.2(j)(2)(xi), categorical eligibility exemptions + href: https://www.law.cornell.edu/cfr/text/7/273.2#j_2_xi - title: 7 CFR 273.9(a), income eligibility standards href: https://www.law.cornell.edu/cfr/text/7/273.9#a breakdown: diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml index 0170a7eeeee..ea7c0c31aa9 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml @@ -12,6 +12,10 @@ CA: # examples 4-5; ACL 14-56; ACL 15-42). Zero-allotment households of three # or more are denied for that reason, not for failing a net test # (ACL 14-63). + # ACL 11-11 expanded MCE to all non-assistance CalFresh households on + # 2011-02-01. Retain 2015-10-01 as this model's BBCE baseline, matching + # the surrounding California gross, asset, and poverty-year parameters; + # this flag alone does not establish modeled eligibility in 2011. 2015-10-01: false CO: 2015-10-01: true @@ -119,6 +123,12 @@ metadata: href: https://www.cdss.ca.gov/lettersnotices/EntRes/getinfo/acl/2015/15-42.pdf#page=4 - title: CDSS Manual of Policies and Procedures 63-409.1, requirements for participation href: https://www.cdss.ca.gov/ord/entres/getinfo/pdf/fsman04b.pdf#page=34 + - title: CDSS June 30, 2014 letter, CalFresh Modified Categorical Eligibility + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/coletters/calfreshmodifiedcategoricaleligibility.pdf#page=3 + - title: CDSS All County Letter 11-11, statewide expansion of MCE to all NACF households + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2011/11-11.pdf + - title: 7 CFR 273.2(j)(2)(xi), categorical eligibility exemptions + href: https://www.law.cornell.edu/cfr/text/7/273.2#j_2_xi - title: 7 CFR 273.9(a), income eligibility standards href: https://www.law.cornell.edu/cfr/text/7/273.9#a breakdown: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml index e09440e9c07..4535b768c19 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml @@ -190,12 +190,10 @@ ca_care_categorically_eligible: false tenant_pays_utilities: false output: - # Countable income includes SNAP. With no work hours entered, this adult - # fails the SNAP ABAWD work requirement through October 2024. In November - # and December, as an MCE household above the net standard, the household - # receives the $23 FY 2025 minimum allotment (CDSS ACL 12-62, example 4): - # 30,000 + 2 x 23. - ca_cpuc_countable_income: 30_046 + # Countable income is $30,000 in annual wages. The two $23 SNAP + # minimum allotments in November and December are excluded under + # 7 USC 2017(b): $30,000 + $0 counted SNAP = $30,000. + ca_cpuc_countable_income: 30_000 ca_care_income_eligible: true # The model's CARE gate treats a household with utilities included in # rent as ineligible, so neither the electricity nor the gas discount is diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml index b0806cf9abf..16183714aee 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml @@ -229,8 +229,8 @@ # and $2,680 for four; MCE gross standard (200%) $2,610 and $5,360; regular # gross standard (130%) $3,483 for four; maximum allotment $298 and $994; # standard deduction $209 (1-3 people) and $223 (4); minimum allotment $24. -# No housing costs, so the excess shelter deduction is zero at these incomes -# whichever utility allowance applies. +# The original cases have no housing costs, so their excess shelter deduction +# is zero at these incomes whichever utility allowance applies. - name: California MCE household of four above the net standard is eligible for the issuance-table allotment. period: 2026-01 absolute_error_margin: 0.1 @@ -304,10 +304,17 @@ # rounded to $1,623, above the $1,305 net standard. snap_net_income: 1_623 meets_snap_net_income_test: false + # Actual medical deduction $202.90 - $35 = $167.90 exceeds the $150 + # standard medical deduction. With no rent, SUA $663 is below half of + # pre-shelter income ($811.55), so the shelter deduction is zero. + snap_excess_medical_expense_deduction: 167.90 + snap_excess_shelter_expense_deduction: 0 # MCE households of one or two get the minimum allotment even above the # net standard (CDSS ACL 13-32, note; ACL 12-62, example 4). + meets_snap_categorical_eligibility: true is_snap_eligible: true - # $298 - ceil(30% x $1,623 = $486.90) < 0, raised to the $24 minimum. + # $298 - ceil(30% x $1,623 = $486.90) = $298 - $487 = -$189, + # raised to the $24 minimum. snap: 24 - name: California elderly person above 200% of poverty is not MCE and fails the net standard. @@ -376,3 +383,51 @@ meets_snap_gross_income_test: false is_snap_eligible: false snap: 0 + +- name: California MCE household of four with rent and no utility allowance receives SNAP above the net standard. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + parent1: + age: 35 + employment_income: 45_000 + weekly_hours_worked_before_lsr: 40 + parent2: + age: 33 + child1: + age: 8 + child2: + age: 4 + spm_units: + spm_unit: + members: [parent1, parent2, child1, child2] + housing_cost: 10_200 + # Isolate MCE from the separate federal heat-and-eat change. The + # household has no utility costs and this case assumes no allowance. + snap_utility_allowance: 0 + tax_units: + tax_unit: + members: [parent1, parent2, child1, child2] + households: + household: + members: [parent1, parent2, child1, child2] + state_code: CA + output: + # Monthly wages $45,000 / 12 = $3,750 are within the $5,360 MCE ceiling. + snap_gross_income: 3_750 + is_tanf_non_cash_eligible: true + # Net before shelter: $3,750 - 20% x $3,750 - $223 = $2,777. + snap_earned_income_deduction: 750 + snap_standard_deduction: 223 + snap_net_income_pre_shelter: 2_777 + # Rent $10,200 / 12 = $850 is below half-net $1,388.50, with no SUA. + snap_excess_shelter_expense_deduction: 0 + snap_net_income: 2_777 + meets_snap_net_income_test: false + # CDSS June 30, 2014 MCE letter, page 3: households of three or more + # receive their issuance-table allotment even above the net standard. + meets_snap_categorical_eligibility: true + is_snap_eligible: true + # $994 - ceil(30% x $2,777 = $833.10) = $994 - $834 = $160. + snap: 160 diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/snap_child_support_integration.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/snap_child_support_integration.yaml index a83935e7ac0..436ac179cf5 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/snap_child_support_integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/snap_child_support_integration.yaml @@ -46,6 +46,9 @@ spm_units: spm_unit: members: [head] + # Isolate California MCE from the separate heat-and-eat change. + # No utility costs are entered and this case assumes no allowance. + snap_utility_allowance: 0 households: household: members: [head] @@ -54,8 +57,19 @@ snap_child_support_gross_income_deduction: 433.33 snap_gross_income: 2_589.63 snap_child_support_deduction: 0 + # The earned deduction uses full wages under 7 CFR 273.9(d)(2): + # $36,275.59 / 12 x 20% = $604.5931667, before excluding child support. + snap_earned_income_deduction: 604.59 + # Net before shelter: $2,589.6325 - $604.5931667 - $209 = $1,776.0393333. + snap_net_income_pre_shelter: 1_776.04 + # Rent $880 is below half-net $888.0196667, so shelter deduction is zero. + snap_excess_shelter_expense_deduction: 0 + snap_net_income: 1_776 + meets_snap_net_income_test: false meets_snap_categorical_eligibility: true is_snap_eligible: true + # MCE retains the one-person minimum: $298 - ceil(30% x $1,776) + # = $298 - $533 = -$235, raised to $24 (CDSS MCE letter, page 3). snap: 24 - name: New York excludes child support from gross income and does not deduct it again from net income. From 8e29c4d952a3b93aabe7b5d90a733e0f64e76e45 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 21:37:14 -0400 Subject: [PATCH 05/17] Fix California SNAP participation for zero-allotment households --- changelog.d/ca-snap-zero-allotment.fixed.md | 1 + .../ca/cdss/snap/test_ca_snap_eligibility.py | 134 +++++++ .../snap/eligibility/is_snap_eligible.yaml | 337 ++++++++++++++++++ .../ca_snap_meets_allotment_requirement.py | 27 ++ .../usda/snap/eligibility/is_snap_eligible.py | 10 +- .../snap/snap_allotment_before_eligibility.py | 21 ++ .../gov/usda/snap/snap_normal_allotment.py | 9 +- 7 files changed, 530 insertions(+), 9 deletions(-) create mode 100644 changelog.d/ca-snap-zero-allotment.fixed.md create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/test_ca_snap_eligibility.py create mode 100644 policyengine_us/variables/gov/states/ca/cdss/snap/eligibility/ca_snap_meets_allotment_requirement.py create mode 100644 policyengine_us/variables/gov/usda/snap/snap_allotment_before_eligibility.py diff --git a/changelog.d/ca-snap-zero-allotment.fixed.md b/changelog.d/ca-snap-zero-allotment.fixed.md new file mode 100644 index 00000000000..2d21035a74d --- /dev/null +++ b/changelog.d/ca-snap-zero-allotment.fixed.md @@ -0,0 +1 @@ +Apply California's zero-allotment participation screen to SNAP households of three or more while preserving Modified Categorical Eligibility's net-income waiver and smaller households' minimum allotments. diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/test_ca_snap_eligibility.py b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/test_ca_snap_eligibility.py new file mode 100644 index 00000000000..c1f340ce58e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/test_ca_snap_eligibility.py @@ -0,0 +1,134 @@ +"""California MCE eligibility must not acquire a net-income ceiling. + +ACL 14-56 (page 6) and ACL 14-63 (page 2) deny zero-allotment households +of three or more, while one- and two-person households retain the minimum. +The YAML examples exercise real income and deductions at selected boundaries. +Hypothesis instead varies two net incomes at the same gross income, household +size and elderly/disabled status, checking the invariant across vectorized +households without supplying eligibility, categorical or member-screen outputs. +Each example has its own simulation and uses the derived California gross +limit. Whole-dollar gross and net inputs isolate this eligibility invariant +from deductions and keep every sampled net income no greater than gross. +""" + +from math import floor + +from hypothesis import example, given, settings, strategies as st + +from policyengine_us import Simulation + +YEAR = 2026 +PERIOD = "2026-01" +# CDSS ACIN I-46-25, attachment I: FY2026 maximum and minimum allotments. +# https://basicneeds.ucmerced.edu/sites/g/files/ufvvjh411/f/documents/CalFreshFiles/ffy_2026_cola_acin_i-46_25.pdf +MAXIMUM_ALLOTMENTS = { + 1: 298, + 2: 546, + 3: 785, + 4: 994, + 5: 1_183, + 6: 1_421, + 7: 1_571, + 8: 1_789, +} +MINIMUM_ALLOTMENT = 24 + + +def _situation(size, elderly, disabled): + people, tax_units, spm_units, households = {}, {}, {}, {} + families, marital_units = {}, {} + for unit in range(2): + members = [] + for member in range(size): + name = f"u{unit}_p{member}" + members.append(name) + age = 65 if elderly and member == 0 else 35 + people[name] = { + "age": {YEAR: age}, + "monthly_age": {PERIOD: age}, + "immigration_status": {YEAR: "CITIZEN"}, + "weekly_hours_worked_before_lsr": {YEAR: 40}, + "is_tax_unit_dependent": {YEAR: False}, + "is_full_time_college_student": {YEAR: False}, + "is_part_time_college_student": {YEAR: False}, + # Derive USDA disability from a qualifying status without + # adding SSI income or categorical eligibility as an input. + "is_permanently_disabled_veteran": {YEAR: disabled and member == 0}, + } + # Unrelated adults buy food together but file separate returns. + tax_units[f"t_{name}"] = {"members": [name]} + families[f"f_{name}"] = {"members": [name]} + marital_units[f"m_{name}"] = {"members": [name]} + spm_units[f"s{unit}"] = {"members": members} + households[f"h{unit}"] = { + "members": members, + "state_code": {YEAR: "CA"}, + } + return { + "people": people, + "tax_units": tax_units, + "spm_units": spm_units, + "families": families, + "marital_units": marital_units, + "households": households, + } + + +@settings(max_examples=40, deadline=None, database=None, derandomize=True) +@given( + size=st.integers(min_value=1, max_value=8), + elderly=st.booleans(), + disabled=st.booleans(), + gross_share=st.integers(min_value=0, max_value=1_000), + nets=st.tuples( + st.integers(min_value=0, max_value=20_000), + st.integers(min_value=0, max_value=20_000), + ), +) +@example(size=1, elderly=False, disabled=False, gross_share=1_000, nets=(0, 20_000)) +@example(size=2, elderly=True, disabled=False, gross_share=1_000, nets=(0, 20_000)) +@example(size=3, elderly=False, disabled=True, gross_share=1_000, nets=(2_613, 2_614)) +@example(size=4, elderly=True, disabled=True, gross_share=1_000, nets=(3_310, 3_311)) +@example(size=5, elderly=False, disabled=False, gross_share=1_000, nets=(0, 20_000)) +@example(size=6, elderly=True, disabled=False, gross_share=1_000, nets=(0, 20_000)) +@example(size=7, elderly=False, disabled=True, gross_share=1_000, nets=(0, 20_000)) +@example(size=8, elderly=True, disabled=True, gross_share=1_000, nets=(0, 20_000)) +def test_net_income_only_changes_mce_participation_through_zero_allotment( + size, elderly, disabled, gross_share, nets +): + simulation = Simulation(situation=_situation(size, elderly, disabled)) + limits = simulation.calculate("tanf_non_cash_gross_income_limit", PERIOD) + gross = [floor(float(limit) * gross_share / 1_000) for limit in limits] + net = [min(value, income) for value, income in zip(nets, gross)] + simulation.set_input("snap_gross_income", PERIOD, gross) + simulation.set_input("snap_net_income", PERIOD, net) + + def calculate(variable): + return simulation.calculate(variable, PERIOD).tolist() + + assert calculate("snap_unit_size") == [size, size] + assert not simulation.calculate("is_snap_excluded_member", PERIOD).any() + assert calculate("is_tanf_non_cash_hheod") == [elderly or disabled] * 2 + assert calculate("snap_gross_test_income") == gross + assert calculate("meets_tanf_non_cash_gross_income_test") == [True, True] + assert calculate("meets_tanf_non_cash_net_income_test") == [True, True] + assert calculate("is_tanf_non_cash_eligible") == [True, True] + assert calculate("meets_snap_categorical_eligibility") == [True, True] + assert calculate("snap_max_allotment") == [MAXIMUM_ALLOTMENTS[size]] * 2 + + # Independent whole-dollar benefit calculation: ceil(30% x net) uses + # integer arithmetic, rather than the production contribution formula. + minimum = MINIMUM_ALLOTMENT if size <= 2 else 0 + expected_allotments = [ + max(minimum, MAXIMUM_ALLOTMENTS[size] - (3 * income + 9) // 10) + for income in net + ] + assert calculate("snap_allotment_before_eligibility") == expected_allotments + assert calculate("snap_normal_allotment") == expected_allotments + eligible = calculate("is_snap_eligible") + assert eligible == [amount > 0 for amount in expected_allotments] + # Holding gross and all non-income gates fixed cannot change eligibility + # unless a three-or-more-person household crosses the zero allotment. + if eligible[0] != eligible[1]: + assert size >= 3 + assert (expected_allotments[0] == 0) != (expected_allotments[1] == 0) diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml index 16183714aee..c6a04af4f07 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml @@ -431,3 +431,340 @@ is_snap_eligible: true # $994 - ceil(30% x $2,777 = $833.10) = $994 - $834 = $160. snap: 160 + # SNAP automatically confers CARE categorical eligibility; CARE excludes FERA. + # Leave CARE categorical eligibility derived to exercise the complete chain. + ca_fera_eligible: false + +# ACL 14-56 p. 6 and ACL 14-63 p. 2: deny zero allotments for 3+; +# retain the minimum for 1-2. FY2026 CDSS maximums: $785 (3), $994 (4). +# Wages $42,330/12 x 80% minus $209 = $2,613; +# maximum minus ceil(30% x net) = $1. A $1 ongoing allotment is issued. +- name: California MCE household of 3 with net income 2613 has a 1 dollar allotment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 35 + employment_income: 42330 + weekly_hours_worked_before_lsr: 40 + member_1: + age: 8 + member_2: + age: 8 + spm_units: + unit: + members: + - earner + - member_1 + - member_2 + housing_cost: 0 + snap_utility_allowance: 0 + tax_units: + unit: + members: + - earner + - member_1 + - member_2 + households: + household: + members: + - earner + - member_1 + - member_2 + state_code: CA + output: + snap_net_income: 2613 + meets_snap_net_income_test: false + is_tanf_non_cash_eligible: true + meets_snap_categorical_eligibility: true + snap_allotment_before_eligibility: 1 + ca_snap_meets_allotment_requirement: true + is_snap_eligible: true + snap: 1 + +# ACL 14-56 p. 6 and ACL 14-63 p. 2: deny zero allotments for 3+; +# retain the minimum for 1-2. FY2026 CDSS maximums: $785 (3), $994 (4). +# Wages $42,345/12 x 80% minus $209 = $2,614; +# maximum minus ceil(30% x net) = $0. The application is denied or participation discontinued. +- name: California MCE household of 3 with net income 2614 has a 0 dollar allotment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 35 + employment_income: 42345 + weekly_hours_worked_before_lsr: 40 + member_1: + age: 8 + member_2: + age: 8 + spm_units: + unit: + members: + - earner + - member_1 + - member_2 + housing_cost: 0 + snap_utility_allowance: 0 + tax_units: + unit: + members: + - earner + - member_1 + - member_2 + households: + household: + members: + - earner + - member_1 + - member_2 + state_code: CA + output: + snap_net_income: 2614 + meets_snap_net_income_test: false + is_tanf_non_cash_eligible: true + meets_snap_categorical_eligibility: true + snap_allotment_before_eligibility: 0 + ca_snap_meets_allotment_requirement: false + is_snap_eligible: false + snap: 0 + +# ACL 14-56 p. 6 and ACL 14-63 p. 2: deny zero allotments for 3+; +# retain the minimum for 1-2. FY2026 CDSS maximums: $785 (3), $994 (4). +# Wages $52,995/12 x 80% minus $223 = $3,310; +# maximum minus ceil(30% x net) = $1. A $1 ongoing allotment is issued. +- name: California MCE household of 4 with net income 3310 has a 1 dollar allotment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 35 + employment_income: 52995 + weekly_hours_worked_before_lsr: 40 + member_1: + age: 8 + member_2: + age: 8 + member_3: + age: 8 + spm_units: + unit: + members: + - earner + - member_1 + - member_2 + - member_3 + housing_cost: 0 + snap_utility_allowance: 0 + tax_units: + unit: + members: + - earner + - member_1 + - member_2 + - member_3 + households: + household: + members: + - earner + - member_1 + - member_2 + - member_3 + state_code: CA + output: + snap_net_income: 3310 + meets_snap_net_income_test: false + is_tanf_non_cash_eligible: true + meets_snap_categorical_eligibility: true + snap_allotment_before_eligibility: 1 + ca_snap_meets_allotment_requirement: true + is_snap_eligible: true + snap: 1 + +# ACL 14-56 p. 6 and ACL 14-63 p. 2: deny zero allotments for 3+; +# retain the minimum for 1-2. FY2026 CDSS maximums: $785 (3), $994 (4). +# Wages $53,010/12 x 80% minus $223 = $3,311; +# maximum minus ceil(30% x net) = $0. The application is denied or participation discontinued. +- name: California MCE household of 4 with net income 3311 has a 0 dollar allotment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 35 + employment_income: 53010 + weekly_hours_worked_before_lsr: 40 + member_1: + age: 8 + member_2: + age: 8 + member_3: + age: 8 + spm_units: + unit: + members: + - earner + - member_1 + - member_2 + - member_3 + housing_cost: 0 + snap_utility_allowance: 0 + tax_units: + unit: + members: + - earner + - member_1 + - member_2 + - member_3 + households: + household: + members: + - earner + - member_1 + - member_2 + - member_3 + state_code: CA + output: + snap_net_income: 3311 + meets_snap_net_income_test: false + is_tanf_non_cash_eligible: true + meets_snap_categorical_eligibility: true + snap_allotment_before_eligibility: 0 + ca_snap_meets_allotment_requirement: false + is_snap_eligible: false + snap: 0 + +# ACL 14-56 p. 6 and ACL 14-63 p. 2: deny zero allotments for 3+; +# retain the minimum for 1-2. FY2026 CDSS maximums: $785 (3), $994 (4). +# Calculated benefit is below the $24 minimum; net exceeds the regular standard. +- name: California MCE household of 1 with net income 1791 has a 24 dollar allotment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 35 + employment_income: 30000 + weekly_hours_worked_before_lsr: 40 + spm_units: + unit: + members: + - earner + housing_cost: 0 + snap_utility_allowance: 0 + tax_units: + unit: + members: + - earner + households: + household: + members: + - earner + state_code: CA + output: + snap_net_income: 1791 + meets_snap_net_income_test: false + is_tanf_non_cash_eligible: true + meets_snap_categorical_eligibility: true + snap_allotment_before_eligibility: 24 + ca_snap_meets_allotment_requirement: true + is_snap_eligible: true + snap: 24 + +# ACL 14-56 p. 6 and ACL 14-63 p. 2: deny zero allotments for 3+; +# retain the minimum for 1-2. FY2026 CDSS maximums: $785 (3), $994 (4). +# Calculated benefit is below the $24 minimum; net exceeds the regular standard. +- name: California MCE household of 2 with net income 1924 has a 24 dollar allotment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 35 + employment_income: 32000 + weekly_hours_worked_before_lsr: 40 + member_1: + age: 8 + spm_units: + unit: + members: + - earner + - member_1 + housing_cost: 0 + snap_utility_allowance: 0 + tax_units: + unit: + members: + - earner + - member_1 + households: + household: + members: + - earner + - member_1 + state_code: CA + output: + snap_net_income: 1924 + meets_snap_net_income_test: false + is_tanf_non_cash_eligible: true + meets_snap_categorical_eligibility: true + snap_allotment_before_eligibility: 24 + ca_snap_meets_allotment_requirement: true + is_snap_eligible: true + snap: 24 + +# ACL 14-56 p. 6 and ACL 14-63 p. 2: deny zero allotments for 3+; +# retain the minimum for 1-2. FY2026 CDSS maximums: $785 (3), $994 (4). +# Review regression: $5,000 gross - $1,000 earned deduction - $223 = $3,777; +# $994 - ceil($1,133.10) is negative, so the allotment and participation are zero. +- name: California MCE household of 4 with net income 3777 has a 0 dollar allotment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + earner: + age: 35 + employment_income: 60000 + weekly_hours_worked_before_lsr: 40 + member_1: + age: 8 + member_2: + age: 8 + member_3: + age: 8 + spm_units: + unit: + members: + - earner + - member_1 + - member_2 + - member_3 + housing_cost: 0 + snap_utility_allowance: 0 + tax_units: + unit: + members: + - earner + - member_1 + - member_2 + - member_3 + households: + household: + members: + - earner + - member_1 + - member_2 + - member_3 + state_code: CA + output: + snap_net_income: 3777 + meets_snap_net_income_test: false + is_tanf_non_cash_eligible: true + meets_snap_categorical_eligibility: true + snap_allotment_before_eligibility: 0 + ca_snap_meets_allotment_requirement: false + is_snap_eligible: false + snap: 0 diff --git a/policyengine_us/variables/gov/states/ca/cdss/snap/eligibility/ca_snap_meets_allotment_requirement.py b/policyengine_us/variables/gov/states/ca/cdss/snap/eligibility/ca_snap_meets_allotment_requirement.py new file mode 100644 index 00000000000..6f5175bd7a4 --- /dev/null +++ b/policyengine_us/variables/gov/states/ca/cdss/snap/eligibility/ca_snap_meets_allotment_requirement.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * + + +class ca_snap_meets_allotment_requirement(Variable): + value_type = bool + entity = SPMUnit + definition_period = MONTH + default_value = True + defined_for = StateCode.CA + label = "Meets California SNAP allotment requirement" + documentation = ( + "California denies or discontinues CE/MCE households of three or more " + "with zero calculated allotments. One- and two-person households retain " + "their minimum allotment. Applies from the model's October 2015 MCE " + "baseline; initial-month proration and issuance exceptions are not modeled." + ) + reference = ( + "https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2014/14-56.pdf#page=6", + "https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2014/14-63.pdf#page=2", + "https://www.law.cornell.edu/cfr/text/7/273.10#e_2_iii", + ) + + def formula_2015_10_01(spm_unit, period, parameters): + size = spm_unit("snap_unit_size", period) + allotment = spm_unit("snap_allotment_before_eligibility", period) + # This is a participation screen, not the regular net-income test. + return (size < 3) | (allotment > 0) diff --git a/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py b/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py index b9c750f0bc1..f08f37c2818 100644 --- a/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py +++ b/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py @@ -31,4 +31,12 @@ def formula(spm_unit, period, parameters): eligible_member_present = spm_unit.any( ~person("is_snap_excluded_member", period) ) - return (normal_eligibility | categorical_eligibility) & eligible_member_present + in_ca = spm_unit.household("state_code", period) == StateCode.CA + ca_allotment_requirement = spm_unit( + "ca_snap_meets_allotment_requirement", period + ) + return ( + (normal_eligibility | categorical_eligibility) + & eligible_member_present + & (~in_ca | ca_allotment_requirement) + ) diff --git a/policyengine_us/variables/gov/usda/snap/snap_allotment_before_eligibility.py b/policyengine_us/variables/gov/usda/snap/snap_allotment_before_eligibility.py new file mode 100644 index 00000000000..cd441016abb --- /dev/null +++ b/policyengine_us/variables/gov/usda/snap/snap_allotment_before_eligibility.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class snap_allotment_before_eligibility(Variable): + value_type = float + entity = SPMUnit + definition_period = MONTH + label = "SNAP allotment before eligibility screening" + documentation = ( + "Calculated SNAP allotment, including the minimum for one- and two-person " + "units, before eligibility screening. This allows zero-allotment " + "participation rules to use the benefit calculation without a cycle." + ) + reference = "https://www.law.cornell.edu/cfr/text/7/273.10#e_2_ii" + unit = USD + + def formula(spm_unit, period, parameters): + expected_contribution = spm_unit("snap_expected_contribution", period) + max_allotment = spm_unit("snap_max_allotment", period) + min_allotment = spm_unit("snap_min_allotment", period) + return max_(min_allotment, max_allotment - expected_contribution) diff --git a/policyengine_us/variables/gov/usda/snap/snap_normal_allotment.py b/policyengine_us/variables/gov/usda/snap/snap_normal_allotment.py index 49c8d33f261..972f42faf67 100644 --- a/policyengine_us/variables/gov/usda/snap/snap_normal_allotment.py +++ b/policyengine_us/variables/gov/usda/snap/snap_normal_allotment.py @@ -13,11 +13,4 @@ class snap_normal_allotment(Variable): unit = USD defined_for = "is_snap_eligible" - def formula(spm_unit, period, parameters): - # Federal SNAP rules are defined in U.S.C Title 7, Chapter 51, which - # also defines state powers to modify the rules. - expected_contribution = spm_unit("snap_expected_contribution", period) - max_allotment = spm_unit("snap_max_allotment", period) - normal_allotment = max_allotment - expected_contribution - min_allotment = spm_unit("snap_min_allotment", period) - return max_(min_allotment, normal_allotment) + adds = ["snap_allotment_before_eligibility"] From 22789328369a42a609242c5812b3c5eb2cd272d3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 21:44:26 -0400 Subject: [PATCH 06/17] Correct California utility integration SNAP cutover arithmetic --- .../policy/baseline/gov/states/ca/cpuc/integration.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml index 4535b768c19..3f1d41c75be 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml @@ -190,9 +190,10 @@ ca_care_categorically_eligible: false tenant_pays_utilities: false output: - # Countable income is $30,000 in annual wages. The two $23 SNAP - # minimum allotments in November and December are excluded under + # Countable income is $30,000 in annual wages. The three $23 SNAP + # minimum allotments in October, November, and December are excluded under # 7 USC 2017(b): $30,000 + $0 counted SNAP = $30,000. + snap: 69 ca_cpuc_countable_income: 30_000 ca_care_income_eligible: true # The model's CARE gate treats a household with utilities included in From a04cc81702f88092d360660551b3d885f97052f8 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 23:42:40 -0400 Subject: [PATCH 07/17] Specify work hours in California utility cutover regression --- .../tests/policy/baseline/gov/states/ca/cpuc/integration.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml index 3f1d41c75be..a8235711b17 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cpuc/integration.yaml @@ -177,6 +177,9 @@ person1: age: 40 employment_income: 30_000 + # Establish the worker exemption independently of the YAML runner's + # treatment of employment_income versus employment_income_before_lsr. + weekly_hours_worked_before_lsr: 30 spm_units: unit: members: [person1] From 141936a768720029cc5988b01e52b6ce0aa1bdf4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 07:10:26 -0400 Subject: [PATCH 08/17] Make SNAP-based CARE and FERA regression discriminating --- .../snap/eligibility/is_snap_eligible.yaml | 86 ++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml index c6a04af4f07..83702b59338 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml @@ -431,8 +431,90 @@ is_snap_eligible: true # $994 - ceil(30% x $2,777 = $833.10) = $994 - $834 = $160. snap: 160 - # SNAP automatically confers CARE categorical eligibility; CARE excludes FERA. - # Leave CARE categorical eligibility derived to exercise the complete chain. + +# CPUC June 2026-May 2027 income tables: CARE $54,640 and FERA $68,300 +# for three people. https://www.cpuc.ca.gov/industries-and-topics/electrical-energy/electric-costs/care-fera-program +# CARE counts both earners: $40,000 + $20,000 = $60,000 > $54,640. +# SNAP excludes the under-18 K-12 student's wages (7 USC 2014(d)(7)): +# https://www.law.cornell.edu/uscode/text/7/2014#d_7 +# CDSS FY2026 table, attachment I: MCE gross $4,442, net standard $2,221, +# standard deduction $209, maximum allotment $785. +# https://www.cdss.ca.gov/Portals/9/Additional-Resources/Letters-and-Notices/ACINs/2025/I-46_25.pdf#page=7 +# SNAP gross $40,000 / 12 = $3,333.33 < $4,442; net rounds from +# $3,333.33 - $666.67 earned deduction - $209 = $2,457.67 to $2,458. +# $2,458 > $2,221, but MCE waives the net test; $785 - ceil($737.40) = $47. +# On main (static comparison), the required net test denies SNAP throughout +# 2026, so CARE categorical/overall eligibility are false and FERA is true. +# With this waiver, SNAP alone confers CARE, which excludes FERA. No rent +# also keeps main's default utility allowance from lowering the annual net +# income enough to confer CARE through SNAP in another month. +- name: California MCE SNAP receipt alone confers CARE above its income limit and excludes FERA. + period: 2026-06 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 35 + employment_income: 40_000 + weekly_hours_worked_before_lsr: 40 + medicaid_enrolled: false + wic: + 2026: 0 + receives_wic: false + ssi: + 2026: 0 + receives_ssi: false + student: + age: 16 + is_in_k12_school: true + employment_income: 20_000 + medicaid_enrolled: false + wic: + 2026: 0 + receives_wic: false + ssi: + 2026: 0 + receives_ssi: false + sibling: + age: 14 + medicaid_enrolled: false + wic: + 2026: 0 + receives_wic: false + ssi: + 2026: 0 + receives_ssi: false + spm_units: + spm_unit: + members: [parent, student, sibling] + housing_cost: 0 + snap_utility_allowance: 0 + receives_snap: + 2026: false + tanf: + 2026: 0 + receives_tanf: false + tax_units: + tax_unit: + members: [parent, student, sibling] + households: + household: + members: [parent, student, sibling] + state_code: CA + is_on_tribal_land: false + output: + snap_gross_income: 3_333.33 + meets_snap_gross_income_test: false + is_tanf_non_cash_eligible: true + snap_net_income: 2_458 + meets_snap_net_income_test: false + meets_snap_categorical_eligibility: true + is_snap_eligible: true + snap: 47 + ca_cpuc_countable_income: 60_000 + ca_care_income_eligible: false + ca_care_categorically_eligible: true + ca_care_eligible: true ca_fera_eligible: false # ACL 14-56 p. 6 and ACL 14-63 p. 2: deny zero allotments for 3+; From 708ee4a1c62e014fa5e7c3991df17899722661a4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 07:18:38 -0400 Subject: [PATCH 09/17] Use calendar-year inputs for the June SNAP and CARE regression --- .../snap/eligibility/is_snap_eligible.yaml | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml index 83702b59338..7f405c5c8de 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml @@ -448,8 +448,9 @@ # With this waiver, SNAP alone confers CARE, which excludes FERA. No rent # also keeps main's default utility allowance from lowering the annual net # income enough to confer CARE through SNAP in another month. +# Use calendar-year inputs for wages and CARE, and assert June SNAP explicitly. - name: California MCE SNAP receipt alone confers CARE above its income limit and excludes FERA. - period: 2026-06 + period: 2026 absolute_error_margin: 0.01 input: people: @@ -503,14 +504,22 @@ state_code: CA is_on_tribal_land: false output: - snap_gross_income: 3_333.33 - meets_snap_gross_income_test: false - is_tanf_non_cash_eligible: true - snap_net_income: 2_458 - meets_snap_net_income_test: false - meets_snap_categorical_eligibility: true - is_snap_eligible: true - snap: 47 + snap_gross_income: + 2026-06: 3_333.33 + meets_snap_gross_income_test: + 2026-06: false + is_tanf_non_cash_eligible: + 2026-06: true + snap_net_income: + 2026-06: 2_458 + meets_snap_net_income_test: + 2026-06: false + meets_snap_categorical_eligibility: + 2026-06: true + is_snap_eligible: + 2026-06: true + snap: + 2026-06: 47 ca_cpuc_countable_income: 60_000 ca_care_income_eligible: false ca_care_categorically_eligible: true From 5c8a2808e1148d49729d35d6f4f1e753ee1d4422 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 14 Oct 2026 15:21:13 -0400 Subject: [PATCH 10/17] Review duplicate changelog: remove merged CARE fragment --- changelog.d/hub-ca-care-excludes-snap-1010.fixed.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 changelog.d/hub-ca-care-excludes-snap-1010.fixed.md diff --git a/changelog.d/hub-ca-care-excludes-snap-1010.fixed.md b/changelog.d/hub-ca-care-excludes-snap-1010.fixed.md deleted file mode 100644 index 084d1437bde..00000000000 --- a/changelog.d/hub-ca-care-excludes-snap-1010.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Exclude SNAP benefits from California CARE and FERA countable income in all modeled years. From 62af20192ea2726408f945fafb1cb292d089008f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 14 Oct 2026 15:21:38 -0400 Subject: [PATCH 11/17] Review allotment requirement: rely on positive calculated allotment --- .../snap/eligibility/ca_snap_meets_allotment_requirement.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/policyengine_us/variables/gov/states/ca/cdss/snap/eligibility/ca_snap_meets_allotment_requirement.py b/policyengine_us/variables/gov/states/ca/cdss/snap/eligibility/ca_snap_meets_allotment_requirement.py index 6f5175bd7a4..e08cd546c6d 100644 --- a/policyengine_us/variables/gov/states/ca/cdss/snap/eligibility/ca_snap_meets_allotment_requirement.py +++ b/policyengine_us/variables/gov/states/ca/cdss/snap/eligibility/ca_snap_meets_allotment_requirement.py @@ -21,7 +21,6 @@ class ca_snap_meets_allotment_requirement(Variable): ) def formula_2015_10_01(spm_unit, period, parameters): - size = spm_unit("snap_unit_size", period) allotment = spm_unit("snap_allotment_before_eligibility", period) # This is a participation screen, not the regular net-income test. - return (size < 3) | (allotment > 0) + return allotment > 0 From 87a11622fb59fc9c8d1c7e14bc038b45f1dd2168 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 14 Oct 2026 15:22:25 -0400 Subject: [PATCH 12/17] Review Python test cost: replace MCE property test with YAML boundaries --- .../ca_snap_meets_allotment_requirement.yaml | 1171 +++++++++++++++++ .../ca/cdss/snap/test_ca_snap_eligibility.py | 134 -- 2 files changed, 1171 insertions(+), 134 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/ca_snap_meets_allotment_requirement.yaml delete mode 100644 policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/test_ca_snap_eligibility.py diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/ca_snap_meets_allotment_requirement.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/ca_snap_meets_allotment_requirement.yaml new file mode 100644 index 00000000000..78e1f03cdcf --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/ca_snap_meets_allotment_requirement.yaml @@ -0,0 +1,1171 @@ +# California MCE has no net-income ceiling for any household size or +# elderly/disabled status (ACL 12-62 examples 4-5; ACL 13-32). ACL 14-56 +# page 6 and ACL 14-63 page 2 deny zero-allotment units of three or more. +# https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2012/12-62.pdf +# https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2013/13-32.pdf +# https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2014/14-56.pdf#page=6 +# https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2014/14-63.pdf#page=2 +# FY2026 maximum/minimum allotments: CDSS ACIN I-46-25 attachment I. +# Each pair holds gross income, size and member eligibility fixed while +# varying only net income. All categorical and member gates are derived. +# Gross inputs floor the model's exact 200%-FPG limit: twice +# ($15,650 + $5,500 x (size - 1))/12. This preserves the existing model's +# exact-ratio convention rather than imposing CDSS chart rounding. +# Expected benefits are hand-calculated as max(minimum, maximum minus +# ceil(30% x net)); minimum is $24 for one/two-person units and $0 otherwise. + +# Net 0 / 2610 gives contributions 0 / 783, +# so maximum $298 yields allotments $298 / $24. +- name: California MCE size 1 without elderly or disabled members varies net income at fixed gross income. + period: 2026-01 + input: + people: + lower_person_1: &first_member_1 + age: 35 + monthly_age: 35 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: false + higher_person_1: + <<: *first_member_1 + spm_units: + lower: + members: [lower_person_1] + snap_gross_income: 2610 + snap_net_income: 0 + higher: + members: [higher_person_1] + snap_gross_income: 2610 + snap_net_income: 2610 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_higher_person_1: + members: [higher_person_1] + families: + family_lower_person_1: + members: [lower_person_1] + family_higher_person_1: + members: [higher_person_1] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_higher_person_1: + members: [higher_person_1] + households: + lower_household: + members: [lower_person_1] + state_code: CA + higher_household: + members: [higher_person_1] + state_code: CA + output: + snap_unit_size: [1, 1] + is_snap_excluded_member: [false, false] + is_tanf_non_cash_hheod: [false, false] + snap_gross_test_income: [2610, 2610] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [298, 298] + snap_allotment_before_eligibility: [298, 24] + ca_snap_meets_allotment_requirement: [true, true] + is_snap_eligible: [true, true] + snap_normal_allotment: [298, 24] + snap: [298, 24] + +# Net 0 / 3525 gives contributions 0 / 1058, +# so maximum $546 yields allotments $546 / $24. +- name: California MCE size 2 elderly varies net income at fixed gross income. + period: 2026-01 + input: + people: + lower_person_1: &first_member_2 + age: 65 + monthly_age: 65 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: false + lower_person_2: + <<: *first_member_2 + age: 35 + monthly_age: 35 + higher_person_1: + <<: *first_member_2 + higher_person_2: + <<: *first_member_2 + age: 35 + monthly_age: 35 + spm_units: + lower: + members: [lower_person_1, lower_person_2] + snap_gross_income: 3525 + snap_net_income: 0 + higher: + members: [higher_person_1, higher_person_2] + snap_gross_income: 3525 + snap_net_income: 3525 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_lower_person_2: + members: [lower_person_2] + tax_higher_person_1: + members: [higher_person_1] + tax_higher_person_2: + members: [higher_person_2] + families: + family_lower_person_1: + members: [lower_person_1] + family_lower_person_2: + members: [lower_person_2] + family_higher_person_1: + members: [higher_person_1] + family_higher_person_2: + members: [higher_person_2] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_lower_person_2: + members: [lower_person_2] + marital_higher_person_1: + members: [higher_person_1] + marital_higher_person_2: + members: [higher_person_2] + households: + lower_household: + members: [lower_person_1, lower_person_2] + state_code: CA + higher_household: + members: [higher_person_1, higher_person_2] + state_code: CA + output: + snap_unit_size: [2, 2] + is_snap_excluded_member: [false, false, false, false] + is_tanf_non_cash_hheod: [true, true] + snap_gross_test_income: [3525, 3525] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [546, 546] + snap_allotment_before_eligibility: [546, 24] + ca_snap_meets_allotment_requirement: [true, true] + is_snap_eligible: [true, true] + snap_normal_allotment: [546, 24] + snap: [546, 24] + +# Net 2613 / 2614 gives contributions 784 / 785, +# so maximum $785 yields allotments $1 / $0. +- name: California MCE size 3 disabled varies net income at fixed gross income. + period: 2026-01 + input: + people: + lower_person_1: &first_member_3 + age: 35 + monthly_age: 35 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: true + lower_person_2: + <<: *first_member_3 + is_permanently_disabled_veteran: false + lower_person_3: + <<: *first_member_3 + is_permanently_disabled_veteran: false + higher_person_1: + <<: *first_member_3 + higher_person_2: + <<: *first_member_3 + is_permanently_disabled_veteran: false + higher_person_3: + <<: *first_member_3 + is_permanently_disabled_veteran: false + spm_units: + lower: + members: [lower_person_1, lower_person_2, lower_person_3] + snap_gross_income: 4441 + snap_net_income: 2613 + higher: + members: [higher_person_1, higher_person_2, higher_person_3] + snap_gross_income: 4441 + snap_net_income: 2614 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_lower_person_2: + members: [lower_person_2] + tax_lower_person_3: + members: [lower_person_3] + tax_higher_person_1: + members: [higher_person_1] + tax_higher_person_2: + members: [higher_person_2] + tax_higher_person_3: + members: [higher_person_3] + families: + family_lower_person_1: + members: [lower_person_1] + family_lower_person_2: + members: [lower_person_2] + family_lower_person_3: + members: [lower_person_3] + family_higher_person_1: + members: [higher_person_1] + family_higher_person_2: + members: [higher_person_2] + family_higher_person_3: + members: [higher_person_3] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_lower_person_2: + members: [lower_person_2] + marital_lower_person_3: + members: [lower_person_3] + marital_higher_person_1: + members: [higher_person_1] + marital_higher_person_2: + members: [higher_person_2] + marital_higher_person_3: + members: [higher_person_3] + households: + lower_household: + members: [lower_person_1, lower_person_2, lower_person_3] + state_code: CA + higher_household: + members: [higher_person_1, higher_person_2, higher_person_3] + state_code: CA + output: + snap_unit_size: [3, 3] + is_snap_excluded_member: [false, false, false, false, false, false] + is_tanf_non_cash_hheod: [true, true] + snap_gross_test_income: [4441, 4441] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [785, 785] + snap_allotment_before_eligibility: [1, 0] + ca_snap_meets_allotment_requirement: [true, false] + is_snap_eligible: [true, false] + snap_normal_allotment: [1, 0] + snap: [1, 0] + +# Net 3310 / 3311 gives contributions 993 / 994, +# so maximum $994 yields allotments $1 / $0. +- name: California MCE size 4 elderly and disabled varies net income at fixed gross income. + period: 2026-01 + input: + people: + lower_person_1: &first_member_4 + age: 65 + monthly_age: 65 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: true + lower_person_2: + <<: *first_member_4 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + lower_person_3: + <<: *first_member_4 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + lower_person_4: + <<: *first_member_4 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_1: + <<: *first_member_4 + higher_person_2: + <<: *first_member_4 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_3: + <<: *first_member_4 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_4: + <<: *first_member_4 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + spm_units: + lower: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4] + snap_gross_income: 5358 + snap_net_income: 3310 + higher: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4] + snap_gross_income: 5358 + snap_net_income: 3311 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_lower_person_2: + members: [lower_person_2] + tax_lower_person_3: + members: [lower_person_3] + tax_lower_person_4: + members: [lower_person_4] + tax_higher_person_1: + members: [higher_person_1] + tax_higher_person_2: + members: [higher_person_2] + tax_higher_person_3: + members: [higher_person_3] + tax_higher_person_4: + members: [higher_person_4] + families: + family_lower_person_1: + members: [lower_person_1] + family_lower_person_2: + members: [lower_person_2] + family_lower_person_3: + members: [lower_person_3] + family_lower_person_4: + members: [lower_person_4] + family_higher_person_1: + members: [higher_person_1] + family_higher_person_2: + members: [higher_person_2] + family_higher_person_3: + members: [higher_person_3] + family_higher_person_4: + members: [higher_person_4] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_lower_person_2: + members: [lower_person_2] + marital_lower_person_3: + members: [lower_person_3] + marital_lower_person_4: + members: [lower_person_4] + marital_higher_person_1: + members: [higher_person_1] + marital_higher_person_2: + members: [higher_person_2] + marital_higher_person_3: + members: [higher_person_3] + marital_higher_person_4: + members: [higher_person_4] + households: + lower_household: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4] + state_code: CA + higher_household: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4] + state_code: CA + output: + snap_unit_size: [4, 4] + is_snap_excluded_member: [false, false, false, false, false, false, false, false] + is_tanf_non_cash_hheod: [true, true] + snap_gross_test_income: [5358, 5358] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [994, 994] + snap_allotment_before_eligibility: [1, 0] + ca_snap_meets_allotment_requirement: [true, false] + is_snap_eligible: [true, false] + snap_normal_allotment: [1, 0] + snap: [1, 0] + +# Net 3940 / 3941 gives contributions 1182 / 1183, +# so maximum $1183 yields allotments $1 / $0. +- name: California MCE size 5 without elderly or disabled members varies net income at fixed gross income. + period: 2026-01 + input: + people: + lower_person_1: &first_member_5 + age: 35 + monthly_age: 35 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: false + lower_person_2: + <<: *first_member_5 + lower_person_3: + <<: *first_member_5 + lower_person_4: + <<: *first_member_5 + lower_person_5: + <<: *first_member_5 + higher_person_1: + <<: *first_member_5 + higher_person_2: + <<: *first_member_5 + higher_person_3: + <<: *first_member_5 + higher_person_4: + <<: *first_member_5 + higher_person_5: + <<: *first_member_5 + spm_units: + lower: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4, lower_person_5] + snap_gross_income: 6275 + snap_net_income: 3940 + higher: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4, higher_person_5] + snap_gross_income: 6275 + snap_net_income: 3941 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_lower_person_2: + members: [lower_person_2] + tax_lower_person_3: + members: [lower_person_3] + tax_lower_person_4: + members: [lower_person_4] + tax_lower_person_5: + members: [lower_person_5] + tax_higher_person_1: + members: [higher_person_1] + tax_higher_person_2: + members: [higher_person_2] + tax_higher_person_3: + members: [higher_person_3] + tax_higher_person_4: + members: [higher_person_4] + tax_higher_person_5: + members: [higher_person_5] + families: + family_lower_person_1: + members: [lower_person_1] + family_lower_person_2: + members: [lower_person_2] + family_lower_person_3: + members: [lower_person_3] + family_lower_person_4: + members: [lower_person_4] + family_lower_person_5: + members: [lower_person_5] + family_higher_person_1: + members: [higher_person_1] + family_higher_person_2: + members: [higher_person_2] + family_higher_person_3: + members: [higher_person_3] + family_higher_person_4: + members: [higher_person_4] + family_higher_person_5: + members: [higher_person_5] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_lower_person_2: + members: [lower_person_2] + marital_lower_person_3: + members: [lower_person_3] + marital_lower_person_4: + members: [lower_person_4] + marital_lower_person_5: + members: [lower_person_5] + marital_higher_person_1: + members: [higher_person_1] + marital_higher_person_2: + members: [higher_person_2] + marital_higher_person_3: + members: [higher_person_3] + marital_higher_person_4: + members: [higher_person_4] + marital_higher_person_5: + members: [higher_person_5] + households: + lower_household: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4, lower_person_5] + state_code: CA + higher_household: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4, higher_person_5] + state_code: CA + output: + snap_unit_size: [5, 5] + is_snap_excluded_member: [false, false, false, false, false, false, false, false, false, false] + is_tanf_non_cash_hheod: [false, false] + snap_gross_test_income: [6275, 6275] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [1183, 1183] + snap_allotment_before_eligibility: [1, 0] + ca_snap_meets_allotment_requirement: [true, false] + is_snap_eligible: [true, false] + snap_normal_allotment: [1, 0] + snap: [1, 0] + +# Net 4733 / 4734 gives contributions 1420 / 1421, +# so maximum $1421 yields allotments $1 / $0. +- name: California MCE size 6 elderly varies net income at fixed gross income. + period: 2026-01 + input: + people: + lower_person_1: &first_member_6 + age: 65 + monthly_age: 65 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: false + lower_person_2: + <<: *first_member_6 + age: 35 + monthly_age: 35 + lower_person_3: + <<: *first_member_6 + age: 35 + monthly_age: 35 + lower_person_4: + <<: *first_member_6 + age: 35 + monthly_age: 35 + lower_person_5: + <<: *first_member_6 + age: 35 + monthly_age: 35 + lower_person_6: + <<: *first_member_6 + age: 35 + monthly_age: 35 + higher_person_1: + <<: *first_member_6 + higher_person_2: + <<: *first_member_6 + age: 35 + monthly_age: 35 + higher_person_3: + <<: *first_member_6 + age: 35 + monthly_age: 35 + higher_person_4: + <<: *first_member_6 + age: 35 + monthly_age: 35 + higher_person_5: + <<: *first_member_6 + age: 35 + monthly_age: 35 + higher_person_6: + <<: *first_member_6 + age: 35 + monthly_age: 35 + spm_units: + lower: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4, lower_person_5, lower_person_6] + snap_gross_income: 7191 + snap_net_income: 4733 + higher: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4, higher_person_5, higher_person_6] + snap_gross_income: 7191 + snap_net_income: 4734 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_lower_person_2: + members: [lower_person_2] + tax_lower_person_3: + members: [lower_person_3] + tax_lower_person_4: + members: [lower_person_4] + tax_lower_person_5: + members: [lower_person_5] + tax_lower_person_6: + members: [lower_person_6] + tax_higher_person_1: + members: [higher_person_1] + tax_higher_person_2: + members: [higher_person_2] + tax_higher_person_3: + members: [higher_person_3] + tax_higher_person_4: + members: [higher_person_4] + tax_higher_person_5: + members: [higher_person_5] + tax_higher_person_6: + members: [higher_person_6] + families: + family_lower_person_1: + members: [lower_person_1] + family_lower_person_2: + members: [lower_person_2] + family_lower_person_3: + members: [lower_person_3] + family_lower_person_4: + members: [lower_person_4] + family_lower_person_5: + members: [lower_person_5] + family_lower_person_6: + members: [lower_person_6] + family_higher_person_1: + members: [higher_person_1] + family_higher_person_2: + members: [higher_person_2] + family_higher_person_3: + members: [higher_person_3] + family_higher_person_4: + members: [higher_person_4] + family_higher_person_5: + members: [higher_person_5] + family_higher_person_6: + members: [higher_person_6] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_lower_person_2: + members: [lower_person_2] + marital_lower_person_3: + members: [lower_person_3] + marital_lower_person_4: + members: [lower_person_4] + marital_lower_person_5: + members: [lower_person_5] + marital_lower_person_6: + members: [lower_person_6] + marital_higher_person_1: + members: [higher_person_1] + marital_higher_person_2: + members: [higher_person_2] + marital_higher_person_3: + members: [higher_person_3] + marital_higher_person_4: + members: [higher_person_4] + marital_higher_person_5: + members: [higher_person_5] + marital_higher_person_6: + members: [higher_person_6] + households: + lower_household: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4, lower_person_5, lower_person_6] + state_code: CA + higher_household: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4, higher_person_5, higher_person_6] + state_code: CA + output: + snap_unit_size: [6, 6] + is_snap_excluded_member: [false, false, false, false, false, false, false, false, false, false, false, false] + is_tanf_non_cash_hheod: [true, true] + snap_gross_test_income: [7191, 7191] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [1421, 1421] + snap_allotment_before_eligibility: [1, 0] + ca_snap_meets_allotment_requirement: [true, false] + is_snap_eligible: [true, false] + snap_normal_allotment: [1, 0] + snap: [1, 0] + +# Net 5233 / 5234 gives contributions 1570 / 1571, +# so maximum $1571 yields allotments $1 / $0. +- name: California MCE size 7 disabled varies net income at fixed gross income. + period: 2026-01 + input: + people: + lower_person_1: &first_member_7 + age: 35 + monthly_age: 35 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: true + lower_person_2: + <<: *first_member_7 + is_permanently_disabled_veteran: false + lower_person_3: + <<: *first_member_7 + is_permanently_disabled_veteran: false + lower_person_4: + <<: *first_member_7 + is_permanently_disabled_veteran: false + lower_person_5: + <<: *first_member_7 + is_permanently_disabled_veteran: false + lower_person_6: + <<: *first_member_7 + is_permanently_disabled_veteran: false + lower_person_7: + <<: *first_member_7 + is_permanently_disabled_veteran: false + higher_person_1: + <<: *first_member_7 + higher_person_2: + <<: *first_member_7 + is_permanently_disabled_veteran: false + higher_person_3: + <<: *first_member_7 + is_permanently_disabled_veteran: false + higher_person_4: + <<: *first_member_7 + is_permanently_disabled_veteran: false + higher_person_5: + <<: *first_member_7 + is_permanently_disabled_veteran: false + higher_person_6: + <<: *first_member_7 + is_permanently_disabled_veteran: false + higher_person_7: + <<: *first_member_7 + is_permanently_disabled_veteran: false + spm_units: + lower: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4, lower_person_5, lower_person_6, lower_person_7] + snap_gross_income: 8108 + snap_net_income: 5233 + higher: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4, higher_person_5, higher_person_6, higher_person_7] + snap_gross_income: 8108 + snap_net_income: 5234 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_lower_person_2: + members: [lower_person_2] + tax_lower_person_3: + members: [lower_person_3] + tax_lower_person_4: + members: [lower_person_4] + tax_lower_person_5: + members: [lower_person_5] + tax_lower_person_6: + members: [lower_person_6] + tax_lower_person_7: + members: [lower_person_7] + tax_higher_person_1: + members: [higher_person_1] + tax_higher_person_2: + members: [higher_person_2] + tax_higher_person_3: + members: [higher_person_3] + tax_higher_person_4: + members: [higher_person_4] + tax_higher_person_5: + members: [higher_person_5] + tax_higher_person_6: + members: [higher_person_6] + tax_higher_person_7: + members: [higher_person_7] + families: + family_lower_person_1: + members: [lower_person_1] + family_lower_person_2: + members: [lower_person_2] + family_lower_person_3: + members: [lower_person_3] + family_lower_person_4: + members: [lower_person_4] + family_lower_person_5: + members: [lower_person_5] + family_lower_person_6: + members: [lower_person_6] + family_lower_person_7: + members: [lower_person_7] + family_higher_person_1: + members: [higher_person_1] + family_higher_person_2: + members: [higher_person_2] + family_higher_person_3: + members: [higher_person_3] + family_higher_person_4: + members: [higher_person_4] + family_higher_person_5: + members: [higher_person_5] + family_higher_person_6: + members: [higher_person_6] + family_higher_person_7: + members: [higher_person_7] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_lower_person_2: + members: [lower_person_2] + marital_lower_person_3: + members: [lower_person_3] + marital_lower_person_4: + members: [lower_person_4] + marital_lower_person_5: + members: [lower_person_5] + marital_lower_person_6: + members: [lower_person_6] + marital_lower_person_7: + members: [lower_person_7] + marital_higher_person_1: + members: [higher_person_1] + marital_higher_person_2: + members: [higher_person_2] + marital_higher_person_3: + members: [higher_person_3] + marital_higher_person_4: + members: [higher_person_4] + marital_higher_person_5: + members: [higher_person_5] + marital_higher_person_6: + members: [higher_person_6] + marital_higher_person_7: + members: [higher_person_7] + households: + lower_household: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4, lower_person_5, lower_person_6, lower_person_7] + state_code: CA + higher_household: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4, higher_person_5, higher_person_6, higher_person_7] + state_code: CA + output: + snap_unit_size: [7, 7] + is_snap_excluded_member: [false, false, false, false, false, false, false, false, false, false, false, false, false, false] + is_tanf_non_cash_hheod: [true, true] + snap_gross_test_income: [8108, 8108] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [1571, 1571] + snap_allotment_before_eligibility: [1, 0] + ca_snap_meets_allotment_requirement: [true, false] + is_snap_eligible: [true, false] + snap_normal_allotment: [1, 0] + snap: [1, 0] + +# Net 5960 / 5961 gives contributions 1788 / 1789, +# so maximum $1789 yields allotments $1 / $0. +- name: California MCE size 8 elderly and disabled varies net income at fixed gross income. + period: 2026-01 + input: + people: + lower_person_1: &first_member_8 + age: 65 + monthly_age: 65 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: true + lower_person_2: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + lower_person_3: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + lower_person_4: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + lower_person_5: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + lower_person_6: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + lower_person_7: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + lower_person_8: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_1: + <<: *first_member_8 + higher_person_2: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_3: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_4: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_5: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_6: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_7: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + higher_person_8: + <<: *first_member_8 + age: 35 + monthly_age: 35 + is_permanently_disabled_veteran: false + spm_units: + lower: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4, lower_person_5, lower_person_6, lower_person_7, lower_person_8] + snap_gross_income: 9025 + snap_net_income: 5960 + higher: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4, higher_person_5, higher_person_6, higher_person_7, higher_person_8] + snap_gross_income: 9025 + snap_net_income: 5961 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_lower_person_2: + members: [lower_person_2] + tax_lower_person_3: + members: [lower_person_3] + tax_lower_person_4: + members: [lower_person_4] + tax_lower_person_5: + members: [lower_person_5] + tax_lower_person_6: + members: [lower_person_6] + tax_lower_person_7: + members: [lower_person_7] + tax_lower_person_8: + members: [lower_person_8] + tax_higher_person_1: + members: [higher_person_1] + tax_higher_person_2: + members: [higher_person_2] + tax_higher_person_3: + members: [higher_person_3] + tax_higher_person_4: + members: [higher_person_4] + tax_higher_person_5: + members: [higher_person_5] + tax_higher_person_6: + members: [higher_person_6] + tax_higher_person_7: + members: [higher_person_7] + tax_higher_person_8: + members: [higher_person_8] + families: + family_lower_person_1: + members: [lower_person_1] + family_lower_person_2: + members: [lower_person_2] + family_lower_person_3: + members: [lower_person_3] + family_lower_person_4: + members: [lower_person_4] + family_lower_person_5: + members: [lower_person_5] + family_lower_person_6: + members: [lower_person_6] + family_lower_person_7: + members: [lower_person_7] + family_lower_person_8: + members: [lower_person_8] + family_higher_person_1: + members: [higher_person_1] + family_higher_person_2: + members: [higher_person_2] + family_higher_person_3: + members: [higher_person_3] + family_higher_person_4: + members: [higher_person_4] + family_higher_person_5: + members: [higher_person_5] + family_higher_person_6: + members: [higher_person_6] + family_higher_person_7: + members: [higher_person_7] + family_higher_person_8: + members: [higher_person_8] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_lower_person_2: + members: [lower_person_2] + marital_lower_person_3: + members: [lower_person_3] + marital_lower_person_4: + members: [lower_person_4] + marital_lower_person_5: + members: [lower_person_5] + marital_lower_person_6: + members: [lower_person_6] + marital_lower_person_7: + members: [lower_person_7] + marital_lower_person_8: + members: [lower_person_8] + marital_higher_person_1: + members: [higher_person_1] + marital_higher_person_2: + members: [higher_person_2] + marital_higher_person_3: + members: [higher_person_3] + marital_higher_person_4: + members: [higher_person_4] + marital_higher_person_5: + members: [higher_person_5] + marital_higher_person_6: + members: [higher_person_6] + marital_higher_person_7: + members: [higher_person_7] + marital_higher_person_8: + members: [higher_person_8] + households: + lower_household: + members: [lower_person_1, lower_person_2, lower_person_3, lower_person_4, lower_person_5, lower_person_6, lower_person_7, lower_person_8] + state_code: CA + higher_household: + members: [higher_person_1, higher_person_2, higher_person_3, higher_person_4, higher_person_5, higher_person_6, higher_person_7, higher_person_8] + state_code: CA + output: + snap_unit_size: [8, 8] + is_snap_excluded_member: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false] + is_tanf_non_cash_hheod: [true, true] + snap_gross_test_income: [9025, 9025] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [1789, 1789] + snap_allotment_before_eligibility: [1, 0] + ca_snap_meets_allotment_requirement: [true, false] + is_snap_eligible: [true, false] + snap_normal_allotment: [1, 0] + snap: [1, 0] + +# Net 2613 / 2614 gives contributions 784 / 785, +# so maximum $785 yields allotments $1 / $0. +- name: Washington keeps categorical eligibility for a zero allotment outside California. + period: 2026-01 + input: + people: + lower_person_1: &first_member_9 + age: 35 + monthly_age: 35 + immigration_status: CITIZEN + weekly_hours_worked_before_lsr: 40 + is_tax_unit_dependent: false + is_full_time_college_student: false + is_part_time_college_student: false + is_permanently_disabled_veteran: false + lower_person_2: + <<: *first_member_9 + lower_person_3: + <<: *first_member_9 + higher_person_1: + <<: *first_member_9 + higher_person_2: + <<: *first_member_9 + higher_person_3: + <<: *first_member_9 + spm_units: + lower: + members: [lower_person_1, lower_person_2, lower_person_3] + snap_gross_income: 4441 + snap_net_income: 2613 + higher: + members: [higher_person_1, higher_person_2, higher_person_3] + snap_gross_income: 4441 + snap_net_income: 2614 + tax_units: + tax_lower_person_1: + members: [lower_person_1] + tax_lower_person_2: + members: [lower_person_2] + tax_lower_person_3: + members: [lower_person_3] + tax_higher_person_1: + members: [higher_person_1] + tax_higher_person_2: + members: [higher_person_2] + tax_higher_person_3: + members: [higher_person_3] + families: + family_lower_person_1: + members: [lower_person_1] + family_lower_person_2: + members: [lower_person_2] + family_lower_person_3: + members: [lower_person_3] + family_higher_person_1: + members: [higher_person_1] + family_higher_person_2: + members: [higher_person_2] + family_higher_person_3: + members: [higher_person_3] + marital_units: + marital_lower_person_1: + members: [lower_person_1] + marital_lower_person_2: + members: [lower_person_2] + marital_lower_person_3: + members: [lower_person_3] + marital_higher_person_1: + members: [higher_person_1] + marital_higher_person_2: + members: [higher_person_2] + marital_higher_person_3: + members: [higher_person_3] + households: + lower_household: + members: [lower_person_1, lower_person_2, lower_person_3] + state_code: WA + higher_household: + members: [higher_person_1, higher_person_2, higher_person_3] + state_code: WA + output: + snap_unit_size: [3, 3] + is_snap_excluded_member: [false, false, false, false, false, false] + is_tanf_non_cash_hheod: [false, false] + snap_gross_test_income: [4441, 4441] + meets_tanf_non_cash_gross_income_test: [true, true] + meets_tanf_non_cash_net_income_test: [true, true] + is_tanf_non_cash_eligible: [true, true] + meets_snap_categorical_eligibility: [true, true] + snap_max_allotment: [785, 785] + snap_allotment_before_eligibility: [1, 0] + ca_snap_meets_allotment_requirement: [true, true] + is_snap_eligible: [true, true] + snap_normal_allotment: [1, 0] + snap: [1, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/test_ca_snap_eligibility.py b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/test_ca_snap_eligibility.py deleted file mode 100644 index c1f340ce58e..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/test_ca_snap_eligibility.py +++ /dev/null @@ -1,134 +0,0 @@ -"""California MCE eligibility must not acquire a net-income ceiling. - -ACL 14-56 (page 6) and ACL 14-63 (page 2) deny zero-allotment households -of three or more, while one- and two-person households retain the minimum. -The YAML examples exercise real income and deductions at selected boundaries. -Hypothesis instead varies two net incomes at the same gross income, household -size and elderly/disabled status, checking the invariant across vectorized -households without supplying eligibility, categorical or member-screen outputs. -Each example has its own simulation and uses the derived California gross -limit. Whole-dollar gross and net inputs isolate this eligibility invariant -from deductions and keep every sampled net income no greater than gross. -""" - -from math import floor - -from hypothesis import example, given, settings, strategies as st - -from policyengine_us import Simulation - -YEAR = 2026 -PERIOD = "2026-01" -# CDSS ACIN I-46-25, attachment I: FY2026 maximum and minimum allotments. -# https://basicneeds.ucmerced.edu/sites/g/files/ufvvjh411/f/documents/CalFreshFiles/ffy_2026_cola_acin_i-46_25.pdf -MAXIMUM_ALLOTMENTS = { - 1: 298, - 2: 546, - 3: 785, - 4: 994, - 5: 1_183, - 6: 1_421, - 7: 1_571, - 8: 1_789, -} -MINIMUM_ALLOTMENT = 24 - - -def _situation(size, elderly, disabled): - people, tax_units, spm_units, households = {}, {}, {}, {} - families, marital_units = {}, {} - for unit in range(2): - members = [] - for member in range(size): - name = f"u{unit}_p{member}" - members.append(name) - age = 65 if elderly and member == 0 else 35 - people[name] = { - "age": {YEAR: age}, - "monthly_age": {PERIOD: age}, - "immigration_status": {YEAR: "CITIZEN"}, - "weekly_hours_worked_before_lsr": {YEAR: 40}, - "is_tax_unit_dependent": {YEAR: False}, - "is_full_time_college_student": {YEAR: False}, - "is_part_time_college_student": {YEAR: False}, - # Derive USDA disability from a qualifying status without - # adding SSI income or categorical eligibility as an input. - "is_permanently_disabled_veteran": {YEAR: disabled and member == 0}, - } - # Unrelated adults buy food together but file separate returns. - tax_units[f"t_{name}"] = {"members": [name]} - families[f"f_{name}"] = {"members": [name]} - marital_units[f"m_{name}"] = {"members": [name]} - spm_units[f"s{unit}"] = {"members": members} - households[f"h{unit}"] = { - "members": members, - "state_code": {YEAR: "CA"}, - } - return { - "people": people, - "tax_units": tax_units, - "spm_units": spm_units, - "families": families, - "marital_units": marital_units, - "households": households, - } - - -@settings(max_examples=40, deadline=None, database=None, derandomize=True) -@given( - size=st.integers(min_value=1, max_value=8), - elderly=st.booleans(), - disabled=st.booleans(), - gross_share=st.integers(min_value=0, max_value=1_000), - nets=st.tuples( - st.integers(min_value=0, max_value=20_000), - st.integers(min_value=0, max_value=20_000), - ), -) -@example(size=1, elderly=False, disabled=False, gross_share=1_000, nets=(0, 20_000)) -@example(size=2, elderly=True, disabled=False, gross_share=1_000, nets=(0, 20_000)) -@example(size=3, elderly=False, disabled=True, gross_share=1_000, nets=(2_613, 2_614)) -@example(size=4, elderly=True, disabled=True, gross_share=1_000, nets=(3_310, 3_311)) -@example(size=5, elderly=False, disabled=False, gross_share=1_000, nets=(0, 20_000)) -@example(size=6, elderly=True, disabled=False, gross_share=1_000, nets=(0, 20_000)) -@example(size=7, elderly=False, disabled=True, gross_share=1_000, nets=(0, 20_000)) -@example(size=8, elderly=True, disabled=True, gross_share=1_000, nets=(0, 20_000)) -def test_net_income_only_changes_mce_participation_through_zero_allotment( - size, elderly, disabled, gross_share, nets -): - simulation = Simulation(situation=_situation(size, elderly, disabled)) - limits = simulation.calculate("tanf_non_cash_gross_income_limit", PERIOD) - gross = [floor(float(limit) * gross_share / 1_000) for limit in limits] - net = [min(value, income) for value, income in zip(nets, gross)] - simulation.set_input("snap_gross_income", PERIOD, gross) - simulation.set_input("snap_net_income", PERIOD, net) - - def calculate(variable): - return simulation.calculate(variable, PERIOD).tolist() - - assert calculate("snap_unit_size") == [size, size] - assert not simulation.calculate("is_snap_excluded_member", PERIOD).any() - assert calculate("is_tanf_non_cash_hheod") == [elderly or disabled] * 2 - assert calculate("snap_gross_test_income") == gross - assert calculate("meets_tanf_non_cash_gross_income_test") == [True, True] - assert calculate("meets_tanf_non_cash_net_income_test") == [True, True] - assert calculate("is_tanf_non_cash_eligible") == [True, True] - assert calculate("meets_snap_categorical_eligibility") == [True, True] - assert calculate("snap_max_allotment") == [MAXIMUM_ALLOTMENTS[size]] * 2 - - # Independent whole-dollar benefit calculation: ceil(30% x net) uses - # integer arithmetic, rather than the production contribution formula. - minimum = MINIMUM_ALLOTMENT if size <= 2 else 0 - expected_allotments = [ - max(minimum, MAXIMUM_ALLOTMENTS[size] - (3 * income + 9) // 10) - for income in net - ] - assert calculate("snap_allotment_before_eligibility") == expected_allotments - assert calculate("snap_normal_allotment") == expected_allotments - eligible = calculate("is_snap_eligible") - assert eligible == [amount > 0 for amount in expected_allotments] - # Holding gross and all non-income gates fixed cannot change eligibility - # unless a three-or-more-person household crosses the zero allotment. - if eligible[0] != eligible[1]: - assert size >= 3 - assert (expected_allotments[0] == 0) != (expected_allotments[1] == 0) From 4326d5f438a7e63bdebd1dc41907e9c7d36b6949 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 14 Oct 2026 15:23:20 -0400 Subject: [PATCH 13/17] Review redundant state check: use California gate defaults --- .../variables/gov/usda/snap/eligibility/is_snap_eligible.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py b/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py index f08f37c2818..67cbb06fb33 100644 --- a/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py +++ b/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py @@ -31,12 +31,11 @@ def formula(spm_unit, period, parameters): eligible_member_present = spm_unit.any( ~person("is_snap_excluded_member", period) ) - in_ca = spm_unit.household("state_code", period) == StateCode.CA ca_allotment_requirement = spm_unit( "ca_snap_meets_allotment_requirement", period ) return ( (normal_eligibility | categorical_eligibility) & eligible_member_present - & (~in_ca | ca_allotment_requirement) + & ca_allotment_requirement ) From 166eedece5d6f279ff3214c1688794c7e6b2cdf6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 14 Oct 2026 15:24:08 -0400 Subject: [PATCH 14/17] Review ACL reference: link to page one --- .../gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml | 2 +- .../hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml index 785e338ef80..8326c5f22b7 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml @@ -126,7 +126,7 @@ metadata: - title: CDSS June 30, 2014 letter, CalFresh Modified Categorical Eligibility href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/coletters/calfreshmodifiedcategoricaleligibility.pdf#page=3 - title: CDSS All County Letter 11-11, statewide expansion of MCE to all NACF households - href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2011/11-11.pdf + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2011/11-11.pdf#page=1 - title: 7 CFR 273.2(j)(2)(xi), categorical eligibility exemptions href: https://www.law.cornell.edu/cfr/text/7/273.2#j_2_xi - title: 7 CFR 273.9(a), income eligibility standards diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml index ea7c0c31aa9..84604db8095 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml @@ -126,7 +126,7 @@ metadata: - title: CDSS June 30, 2014 letter, CalFresh Modified Categorical Eligibility href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/coletters/calfreshmodifiedcategoricaleligibility.pdf#page=3 - title: CDSS All County Letter 11-11, statewide expansion of MCE to all NACF households - href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2011/11-11.pdf + href: https://www.cdss.ca.gov/lettersnotices/entres/getinfo/acl/2011/11-11.pdf#page=1 - title: 7 CFR 273.2(j)(2)(xi), categorical eligibility exemptions href: https://www.law.cornell.edu/cfr/text/7/273.2#j_2_xi - title: 7 CFR 273.9(a), income eligibility standards From 5244a2566677433190c13b8fa96ad9fa75168fca Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 14 Oct 2026 15:24:42 -0400 Subject: [PATCH 15/17] Review fixture inputs: remove redundant calendar-year keys --- .../snap/eligibility/is_snap_eligible.yaml | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml index 7f405c5c8de..455ae683e00 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml @@ -459,41 +459,33 @@ employment_income: 40_000 weekly_hours_worked_before_lsr: 40 medicaid_enrolled: false - wic: - 2026: 0 + wic: 0 receives_wic: false - ssi: - 2026: 0 + ssi: 0 receives_ssi: false student: age: 16 is_in_k12_school: true employment_income: 20_000 medicaid_enrolled: false - wic: - 2026: 0 + wic: 0 receives_wic: false - ssi: - 2026: 0 + ssi: 0 receives_ssi: false sibling: age: 14 medicaid_enrolled: false - wic: - 2026: 0 + wic: 0 receives_wic: false - ssi: - 2026: 0 + ssi: 0 receives_ssi: false spm_units: spm_unit: members: [parent, student, sibling] housing_cost: 0 snap_utility_allowance: 0 - receives_snap: - 2026: false - tanf: - 2026: 0 + receives_snap: false + tanf: 0 receives_tanf: false tax_units: tax_unit: From 55899ea8e97f432d5e863679df09ea9ffe94f2d7 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 14 Oct 2026 15:33:06 -0400 Subject: [PATCH 16/17] Review YAML replacement: use exact single-person gross limit --- .../cdss/snap/ca_snap_meets_allotment_requirement.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/ca_snap_meets_allotment_requirement.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/ca_snap_meets_allotment_requirement.yaml index 78e1f03cdcf..6321f72868f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/ca_snap_meets_allotment_requirement.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/snap/ca_snap_meets_allotment_requirement.yaml @@ -14,7 +14,7 @@ # Expected benefits are hand-calculated as max(minimum, maximum minus # ceil(30% x net)); minimum is $24 for one/two-person units and $0 otherwise. -# Net 0 / 2610 gives contributions 0 / 783, +# Net 0 / 2608 gives contributions 0 / 783, # so maximum $298 yields allotments $298 / $24. - name: California MCE size 1 without elderly or disabled members varies net income at fixed gross income. period: 2026-01 @@ -34,12 +34,12 @@ spm_units: lower: members: [lower_person_1] - snap_gross_income: 2610 + snap_gross_income: 2608 snap_net_income: 0 higher: members: [higher_person_1] - snap_gross_income: 2610 - snap_net_income: 2610 + snap_gross_income: 2608 + snap_net_income: 2608 tax_units: tax_lower_person_1: members: [lower_person_1] @@ -66,7 +66,7 @@ snap_unit_size: [1, 1] is_snap_excluded_member: [false, false] is_tanf_non_cash_hheod: [false, false] - snap_gross_test_income: [2610, 2610] + snap_gross_test_income: [2608, 2608] meets_tanf_non_cash_gross_income_test: [true, true] meets_tanf_non_cash_net_income_test: [true, true] is_tanf_non_cash_eligible: [true, true] From 8f3369be39447c6a918328ca1e94d6b858106279 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 15 Oct 2026 00:53:07 -0400 Subject: [PATCH 17/17] Review partner request: update 24 required CalFresh contract outcomes --- .../edge_cases/federal/nutrition/snap/ca.yaml | 41 +++++----- .../analytics_coverage/signatures/ca.yaml | 60 +++++++++----- .../signatures/federal.yaml | 78 ++++++++++++------- 3 files changed, 112 insertions(+), 67 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/nutrition/snap/ca.yaml b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/nutrition/snap/ca.yaml index da65546cf34..a7b1dd4aa89 100644 --- a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/nutrition/snap/ca.yaml +++ b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/nutrition/snap/ca.yaml @@ -1,15 +1,15 @@ # California (CalFresh) cases that differ from the federal.yaml (TX) baseline # for state-policy reasons: -# - CA applies the BBCE NET income test (net_applies CA = true), so the binding -# eligibility boundary is net income at 100% of the poverty guideline, not -# the 200% BBCE gross limit. -# - CA is an always-SUA state (SUA $663 in FY2026, applied without utility -# expense inputs). +# - CA MCE waives the net income test and uses a 200% poverty gross limit. +# Units with three or more members also need a positive allotment. +# - CA's FY2026 SUA is $663, but after the federal heat-and-eat restriction +# it requires heating/cooling expenses or elderly/disabled deeming. +# The households below have no qualifying utility expenses. # - CA extends CalFresh to additional immigration statuses (CFAP) and delays # the OBBB refugee exclusion to 2026-04-01 (ACL 25-92). -# SNAP compares income to the poverty guideline in effect at the preceding -# Oct 1: Jan-Sep 2026 use the 2025 FPG (3-person $26,650, i.e. a monthly net -# limit of $2,220.83); Oct-Dec 2026 use the 2026 FPG ($27,320 -> $2,276.67). +# SNAP uses the poverty guideline in effect at the preceding Oct 1: +# Jan-Sep 2026 use the 2025 FPG (3-person $26,650); Oct-Dec use the 2026 FPG +# ($27,320). The MCE gross limits are twice those guidelines. - name: snap_ca_net_income_just_below_fy2026_net_limit (ca) period: 2026 absolute_error_margin: 0.1 @@ -71,9 +71,9 @@ spm_units: spm_unit: # Gross ratio 36,000/26,650 = 1.35 fails the federal 130% test, but CA - # BBCE (200% gross + net test) rescues: net = 36,000/12 x 0.8 - 209 = - # 2,191.00 (SUA 663 < half of pre-shelter net, so no shelter deduction) - # <= 2,220.83 Jan-Sep limit -> eligible all year. EC = ceil(2,191 x + # MCE's 200% gross limit and waived net test permit participation. + # Net = 36,000/12 x 0.8 - 209 = 2,191.00, with no utility allowance + # or shelter deduction. EC = ceil(2,191 x # 0.3) = 658 -> 785 - 658 = 127.00/month Jan-Sep; Oct-Dec (FY2027): net # 2,400 - 217 = 2,183 -> EC ceil(654.90) = 655 -> 808 - 655 = 153.00. # Annual = 9 x 127.00 + 3 x 153.00 = 1,602.00. @@ -139,8 +139,8 @@ output: spm_units: spm_unit: - # At the binding CA net limit: net = 36,447/12 x 0.8 - 209 = 2,220.80, - # just under the Jan-Sep limit of 26,650/12 = 2,220.83 -> eligible. + # At the former model net limit: 36,447/12 x 0.8 - 209 = 2,220.80. + # MCE waives that test; the gross limit and positive allotment apply. # EC = ceil(2,221 x 0.3) = 667 -> 785 - 667 = 118.00/month # Jan-Sep; Oct-Dec (FY2027): net 2,212.80 rounds to 2,213 -> EC 664 -> # 808 - 664 = 144.00. Annual = 9 x 118.00 + 3 x 144.00 = 1,494.00. @@ -206,14 +206,13 @@ output: spm_units: spm_unit: - # Deliberate ineligible-side pin of CA's net-test route: net = - # 38,000/12 x 0.8 - 209 = 2,324.33 exceeds the net limit in every - # month (Jan-Sep 2,220.83; Oct-Dec: 2,316.95 > 2,276.67), and the - # federal 130% gross test also fails (ratio 1.43), so the unit is - # ineligible all year. In a no-net-test 200% state this income would - # be eligible. - snap: 0.0 - is_snap_eligible: false + # MCE waives the net test: $38,000 is below the 200% gross limit. + # No utility or shelter deduction; net before rounding is + # $2,324.33/$2,316.33. Positive allotments permit participation. + # FY2026/FY2027 net $2,324/$2,316; contribution $698/$695. + # SNAP $87/$113/month; annual 9 x 87 + 3 x 113 = $1,122. + snap: 1122.0 + is_snap_eligible: true - name: snap_ca_head_immigration_refugee_cfap_transition (ca) period: 2026 absolute_error_margin: 0.1 diff --git a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/signatures/ca.yaml b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/signatures/ca.yaml index ba2c6a460ad..310c1e633cd 100644 --- a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/signatures/ca.yaml +++ b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/signatures/ca.yaml @@ -560,11 +560,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -936,11 +938,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -1903,11 +1907,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,946/$2,936; contribution $884/$881. + # SNAP $110/$142/month; annual 9 x 110 + 3 x 142 = $1,416. + snap: 1416.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -2291,11 +2297,13 @@ ca_tanf_eligible: false housing_assistance: 0.0 is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -2674,11 +2682,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,946/$2,936; contribution $884/$881. + # SNAP $110/$142/month; annual 9 x 110 + 3 x 142 = $1,416. + snap: 1416.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -2874,11 +2884,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,946/$2,936; contribution $884/$881. + # SNAP $110/$142/month; annual 9 x 110 + 3 x 142 = $1,416. + snap: 1416.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -3258,11 +3270,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -4599,11 +4613,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -4795,11 +4811,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,946/$2,936; contribution $884/$881. + # SNAP $110/$142/month; annual 9 x 110 + 3 x 142 = $1,416. + snap: 1416.0 people: head: ca_ala_general_assistance_eligible_person: false @@ -4993,11 +5011,13 @@ ca_tanf: 0.0 ca_tanf_eligible: false is_lifeline_eligible: true - is_snap_eligible: false + is_snap_eligible: true la_general_relief: 0.0 la_general_relief_eligible: false lifeline: 228.0 - snap: 0.0 + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: ca_ala_general_assistance_eligible_person: false diff --git a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/signatures/federal.yaml b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/signatures/federal.yaml index eb5e62d1ebb..365f6606236 100644 --- a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/signatures/federal.yaml +++ b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/signatures/federal.yaml @@ -167,8 +167,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: is_wic_eligible: false @@ -352,8 +354,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,796/$2,781; contribution $839/$835. + # SNAP $155/$188/month; annual 9 x 155 + 3 x 188 = $1,959. + snap: 1959.0 people: head: is_wic_eligible: false @@ -514,8 +518,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,966/$2,957; contribution $890/$888. + # SNAP $104/$135/month; annual 9 x 104 + 3 x 135 = $1,341. + snap: 1341.0 people: head: is_wic_eligible: false @@ -617,8 +623,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,966/$2,957; contribution $890/$888. + # SNAP $104/$135/month; annual 9 x 104 + 3 x 135 = $1,341. + snap: 1341.0 people: head: is_wic_eligible: false @@ -724,8 +732,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: is_wic_eligible: false @@ -1048,8 +1058,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: is_wic_eligible: false @@ -1148,8 +1160,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,946/$2,936; contribution $884/$881. + # SNAP $110/$142/month; annual 9 x 110 + 3 x 142 = $1,416. + snap: 1416.0 people: head: is_wic_eligible: false @@ -1248,8 +1262,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: is_wic_eligible: false @@ -1348,8 +1364,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,966/$2,957; contribution $890/$888. + # SNAP $104/$135/month; annual 9 x 104 + 3 x 135 = $1,341. + snap: 1341.0 people: head: is_wic_eligible: false @@ -1455,8 +1473,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: is_wic_eligible: false @@ -1550,8 +1570,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,977/$2,971; contribution $894/$892. + # SNAP $100/$131/month; annual 9 x 100 + 3 x 131 = $1,293. + snap: 1293.0 people: head: is_wic_eligible: false @@ -1649,8 +1671,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,966/$2,957; contribution $890/$888. + # SNAP $104/$135/month; annual 9 x 104 + 3 x 135 = $1,341. + snap: 1341.0 people: head: is_wic_eligible: false @@ -1760,8 +1784,10 @@ output: spm_units: spm_unit: - is_snap_eligible: false - snap: 0.0 + is_snap_eligible: true + # FY2026/FY2027 net $2,796/$2,781; contribution $839/$835. + # SNAP $155/$188/month; annual 9 x 155 + 3 x 188 = $1,959. + snap: 1959.0 people: head: is_wic_eligible: false