From 878739c953a1a29796e7bdf60a9bf917fe150dd4 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 9 Jul 2026 10:18:52 -0400 Subject: [PATCH 1/4] Key TX CEAP categorical eligibility on calculated benefit receipt 42 USC 8624(b)(2)(A) grants LIHEAP categorical eligibility to households where a member receives TANF, SNAP, or SSI. The TANF leg read the bare is_tanf_enrolled input (always false in microsimulation, so the pathway was dead), the SNAP leg read is_snap_eligible, and the SSI leg read is_ssi_eligible, which has no income test and so qualified non-recipients. All three now key on the calculated benefit being positive, matching the statute's receipt standard. Co-Authored-By: Claude Fable 5 --- .../tx-ceap-categorical-receipt.fixed.md | 1 + .../tx/tdhca/ceap/tx_ceap_eligible.yaml | 35 ++++++++++++++++--- .../states/tx/tdhca/ceap/tx_ceap_eligible.py | 13 +++---- 3 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 changelog.d/tx-ceap-categorical-receipt.fixed.md diff --git a/changelog.d/tx-ceap-categorical-receipt.fixed.md b/changelog.d/tx-ceap-categorical-receipt.fixed.md new file mode 100644 index 00000000000..e0564e7479e --- /dev/null +++ b/changelog.d/tx-ceap-categorical-receipt.fixed.md @@ -0,0 +1 @@ +Key Texas Comprehensive Energy Assistance Program categorical eligibility on calculated TANF, SNAP, and SSI receipt per 42 USC 8624(b)(2)(A), replacing the enrollment-flag and eligibility-only checks. diff --git a/policyengine_us/tests/policy/baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml index 4c60f9e22fe..0570bbcf65c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml @@ -17,7 +17,6 @@ employment_income: 60_000 spm_unit_size: 4 state_code: TX - is_snap_eligible: false output: tx_ceap_eligible: false @@ -60,17 +59,45 @@ employment_income: 50_000 spm_unit_size: 4 state_code: TX - is_snap_eligible: false output: tx_ceap_eligible: false -- name: Case 7, SNAP-eligible household above income limit is categorically eligible. +- name: Case 7, household receiving SNAP above the income limit is categorically eligible. absolute_error_margin: 0.1 period: 2024 input: employment_income: 80_000 spm_unit_size: 1 state_code: TX - is_snap_eligible: true + snap: 2_400 + output: + tx_ceap_eligible: true + +- name: Case 8, household receiving TANF above the income limit is categorically eligible. + period: 2024 + input: + employment_income: 80_000 + spm_unit_size: 1 + state_code: TX + tanf: 1_200 + output: + tx_ceap_eligible: true + +- name: Case 9, household with a member receiving SSI above the income limit is categorically eligible. + period: 2024 + input: + people: + person1: + employment_income: 80_000 + ssi: 0 + person2: + ssi: 9_000 + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: TX output: tx_ceap_eligible: true diff --git a/policyengine_us/variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py b/policyengine_us/variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py index ea200411c75..37e699039f0 100644 --- a/policyengine_us/variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py +++ b/policyengine_us/variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py @@ -31,12 +31,13 @@ def formula(spm_unit, period, parameters): income_limit = max_(fpg_limit, smi_limit) income_eligible = income <= income_limit - # Categorical eligibility per 42 USC 8624(b)(2)(A) - # and FY 2024 State Plan Section 1.4 - tanf = spm_unit("is_tanf_enrolled", period) - snap = spm_unit("is_snap_eligible", period) - person = spm_unit.members - ssi = spm_unit.any(person("is_ssi_eligible", period)) + # Categorical eligibility per 42 USC 8624(b)(2)(A) and FY 2024 + # State Plan Section 1.4 — households where a member receives + # TANF, SNAP, or SSI, so the check keys on the calculated + # benefits rather than eligibility or enrollment flags. + tanf = spm_unit("tanf", period) > 0 + snap = spm_unit("snap", period) > 0 + ssi = add(spm_unit, period, ["ssi"]) > 0 categorically_eligible = tanf | snap | ssi return income_eligible | categorically_eligible From 9e13e168df1eecf1bf01d8d09c99652beeaa0870 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 9 Jul 2026 10:54:01 -0400 Subject: [PATCH 2/4] Isolate income-pathway tests from BBCE SNAP and trim comment Texas BBCE makes the size-4 test households SNAP-positive at these incomes, which correctly grants categorical eligibility under the new receipt check but breaks the cases that exercise the income limits. Set snap to zero in those cases, mirroring the original author's is_snap_eligible isolation; the receipt pathway keeps its own cases. Co-Authored-By: Claude Fable 5 --- .../baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml | 4 ++++ .../variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml index 0570bbcf65c..a2982654b00 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/tx/tdhca/ceap/tx_ceap_eligible.yaml @@ -17,6 +17,8 @@ employment_income: 60_000 spm_unit_size: 4 state_code: TX + # Isolate the income pathway: BBCE can make these incomes SNAP-positive. + snap: 0 output: tx_ceap_eligible: false @@ -59,6 +61,8 @@ employment_income: 50_000 spm_unit_size: 4 state_code: TX + # Isolate the income pathway: BBCE makes this income SNAP-positive. + snap: 0 output: tx_ceap_eligible: false diff --git a/policyengine_us/variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py b/policyengine_us/variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py index 37e699039f0..2ea6afba4b4 100644 --- a/policyengine_us/variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py +++ b/policyengine_us/variables/gov/states/tx/tdhca/ceap/tx_ceap_eligible.py @@ -32,9 +32,8 @@ def formula(spm_unit, period, parameters): income_eligible = income <= income_limit # Categorical eligibility per 42 USC 8624(b)(2)(A) and FY 2024 - # State Plan Section 1.4 — households where a member receives - # TANF, SNAP, or SSI, so the check keys on the calculated - # benefits rather than eligibility or enrollment flags. + # State Plan Section 1.4: households where a member receives + # TANF, SNAP, or SSI. tanf = spm_unit("tanf", period) > 0 snap = spm_unit("snap", period) > 0 ssi = add(spm_unit, period, ["ssi"]) > 0 From 8d4abcec83a8325068819316b0947f763ab31e38 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 9 Jul 2026 10:59:18 -0400 Subject: [PATCH 3/4] Key remaining receipt checks on calculated TANF Extends the receipt convention to the other three checks that read the bare is_tanf_enrolled input (always false in microsimulation): the Medicaid community engagement TANF pass-through, the SNAP work registration TANF work-requirements exemption per 7 CFR 273.7(b)(1)(iii), and the Oklahoma sales tax relief credit TANF exclusion. All three now gate on the calculated tanf amount; none has a circular dependency on its consumers. WA Birth to Three ECEAP was audited and deliberately left unchanged: RCW 43.216.578 qualifies families "eligible for or ... receiving" basic food, so its is_snap_eligible check matches the statute. Co-Authored-By: Claude Fable 5 --- .../tx-ceap-categorical-receipt.fixed.md | 2 +- ...nity_engagement_pass_through_eligible.yaml | 2 +- .../gov/states/ok/tax/income/ok_stc.yaml | 26 +++++++++++++++++++ ...snap_work_registration_exempt_non_age.yaml | 12 ++++----- ...munity_engagement_pass_through_eligible.py | 2 +- .../states/ok/tax/income/credits/ok_stc.py | 8 +++--- ...s_snap_work_registration_exempt_non_age.py | 13 +++++----- 7 files changed, 44 insertions(+), 21 deletions(-) diff --git a/changelog.d/tx-ceap-categorical-receipt.fixed.md b/changelog.d/tx-ceap-categorical-receipt.fixed.md index e0564e7479e..487e52a6dc5 100644 --- a/changelog.d/tx-ceap-categorical-receipt.fixed.md +++ b/changelog.d/tx-ceap-categorical-receipt.fixed.md @@ -1 +1 @@ -Key Texas Comprehensive Energy Assistance Program categorical eligibility on calculated TANF, SNAP, and SSI receipt per 42 USC 8624(b)(2)(A), replacing the enrollment-flag and eligibility-only checks. +Key categorical eligibility and receipt checks on calculated benefits: TX CEAP (42 USC 8624(b)(2)(A)), the Medicaid community engagement TANF pass-through, the SNAP work registration TANF exemption (7 CFR 273.7(b)(1)(iii)), and the Oklahoma sales tax relief credit TANF exclusion. diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/eligibility/medicaid_community_engagement_pass_through_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/eligibility/medicaid_community_engagement_pass_through_eligible.yaml index 50d53c99241..32e272d4de2 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/eligibility/medicaid_community_engagement_pass_through_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/eligibility/medicaid_community_engagement_pass_through_eligible.yaml @@ -26,7 +26,7 @@ spm_units: spm_unit: members: [person1] - is_tanf_enrolled: true + tanf: 100 meets_tanf_work_requirements: true households: household: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_stc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_stc.yaml index 9fdae1540a5..c2f042d5293 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_stc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_stc.yaml @@ -219,3 +219,29 @@ # elderly $50,000 limit and restoring the $40-per-person credit. ok_gross_income: 38_565.84 ok_stc: 80 + +- name: TANF recipients are excluded from the credit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 15_000 + person2: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 1_200 + households: + household: + members: [person1, person2] + state_code: OK + output: + # Income qualifies under Pathway 1, but TANF receipt excludes the unit. + ok_stc: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.yaml index 989f6389dc0..0328ca10863 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.yaml @@ -117,7 +117,7 @@ # --- (iii) Subject to and complying with TANF work requirements --- -- name: Case 9, TANF enrollee complying with TANF work requirements is exempt. +- name: Case 9, TANF recipient complying with TANF work requirements is exempt. period: 2026-01 input: people: @@ -128,11 +128,11 @@ spm_units: spm_unit: members: [person1] - is_tanf_enrolled: true + tanf: 100 output: is_snap_work_registration_exempt_non_age: true -- name: Case 10, complying with TANF work requirements but not enrolled in TANF is not exempt. +- name: Case 10, complying with TANF work requirements but not receiving TANF is not exempt. period: 2026-01 input: people: @@ -143,11 +143,11 @@ spm_units: spm_unit: members: [person1] - is_tanf_enrolled: false + tanf: 0 output: is_snap_work_registration_exempt_non_age: false -- name: Case 11, TANF enrollee not complying with TANF work requirements is not exempt. +- name: Case 11, TANF recipient not complying with TANF work requirements is not exempt. period: 2026-01 input: people: @@ -158,7 +158,7 @@ spm_units: spm_unit: members: [person1] - is_tanf_enrolled: true + tanf: 100 output: is_snap_work_registration_exempt_non_age: false diff --git a/policyengine_us/variables/gov/hhs/medicaid/eligibility/medicaid_community_engagement_pass_through_eligible.py b/policyengine_us/variables/gov/hhs/medicaid/eligibility/medicaid_community_engagement_pass_through_eligible.py index df5768470a9..d29502f24eb 100644 --- a/policyengine_us/variables/gov/hhs/medicaid/eligibility/medicaid_community_engagement_pass_through_eligible.py +++ b/policyengine_us/variables/gov/hhs/medicaid/eligibility/medicaid_community_engagement_pass_through_eligible.py @@ -14,7 +14,7 @@ class medicaid_community_engagement_pass_through_eligible(Variable): def formula(person, period, parameters): snap_work = parameters(period).gov.usda.snap.work_requirements snap = person.spm_unit("snap", period) > 0 - tanf = person.spm_unit("is_tanf_enrolled", period) + tanf = person.spm_unit("tanf", period) > 0 age = person("monthly_age", period) # Cast to bool: single_amount bool brackets return int (0/1), which diff --git a/policyengine_us/variables/gov/states/ok/tax/income/credits/ok_stc.py b/policyengine_us/variables/gov/states/ok/tax/income/credits/ok_stc.py index c4988fec04d..eb3eedaf0f9 100644 --- a/policyengine_us/variables/gov/states/ok/tax/income/credits/ok_stc.py +++ b/policyengine_us/variables/gov/states/ok/tax/income/credits/ok_stc.py @@ -63,11 +63,9 @@ class ok_stc(Variable): def formula(tax_unit, period, parameters): # For details, see Form 538-S in the 511 packets referenced above p = parameters(period).gov.states.ok.tax.income.credits.sales_tax - # Exclusion: TANF recipients are not eligible. Use actual enrollment - # (is_tanf_enrolled) rather than the modeled ok_tanf benefit, so that - # tax filers who are not receiving TANF still qualify even when the - # model would impute TANF eligibility for them. - tanf_ineligible = add(tax_unit, period, ["is_tanf_enrolled"]) > 0 + # Exclusion: TANF recipients are not eligible, keyed on the + # calculated tanf benefit. + tanf_ineligible = add(tax_unit, period, ["tanf"]) > 0 # Get gross household income for eligibility tests income = tax_unit("ok_gross_income", period) # Pathway 1: Low income (gross income <= $20,000) diff --git a/policyengine_us/variables/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.py b/policyengine_us/variables/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.py index 257b5ee023e..db8873890e9 100644 --- a/policyengine_us/variables/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.py +++ b/policyengine_us/variables/gov/usda/snap/eligibility/work_requirements/is_snap_work_registration_exempt_non_age.py @@ -21,13 +21,12 @@ def formula(person, period, parameters): # (ii) Physically or mentally unfit for employment is_disabled = person("is_disabled", period) # (iii) Subject to and complying with TANF work requirements. - # TANF enrollment is an existing SPM-unit input; person-level - # compliance is a documented input the data layer may not yet - # populate (PolicyEngine/populace#244). We gate on enrollment - # because a person cannot be subject to TANF work requirements - # without receiving TANF. - complying_with_tanf_work_requirements = person.spm_unit( - "is_tanf_enrolled", period + # Person-level compliance is a documented input the data layer + # may not yet populate (PolicyEngine/populace#244). We gate on + # calculated TANF receipt because a person cannot be subject to + # TANF work requirements without receiving TANF. + complying_with_tanf_work_requirements = ( + person.spm_unit("tanf", period) > 0 ) & person("is_complying_with_tanf_work_requirements", period) # (iv) Responsible for care of dependent child under 6 is_dependent = person("is_tax_unit_dependent", period) From 9758ea61bc597e788b71692e44cbb6fd4f2561bb Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 9 Jul 2026 11:47:22 -0400 Subject: [PATCH 4/4] Update OK STC tests for the calculated-TANF exclusion The taxsim#988 parity case asserted that a family the model imputes as TANF-eligible keeps the credit; under the calculated-receipt exclusion that family's non-receipt is expressed by zeroing the tanf amount, as an API user who knows actual receipt would. The 2021 recipient case switches its now-inert is_tanf_enrolled input to a tanf amount so it excludes by receipt rather than by coincidence of the modeled benefit. Co-Authored-By: Claude Fable 5 --- .../baseline/gov/states/ok/tax/income/ok_stc.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_stc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_stc.yaml index c2f042d5293..2e35a986b08 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_stc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_stc.yaml @@ -49,7 +49,7 @@ spm_units: spm_unit: members: [head, child] - is_tanf_enrolled: true + tanf: 1_200 households: household: members: [head, child] @@ -58,7 +58,7 @@ # Actual TANF recipients excluded (TANF includes sales tax relief) ok_stc: 0 -- name: OK STC - 2025 - modeled-TANF-eligible family still gets credit +- name: OK STC - 2025 - family not receiving TANF still gets credit period: 2025 input: people: @@ -78,14 +78,15 @@ spm_units: spm_unit: members: [head, spouse, child1, child2, child3, child4] + # The family does not actually receive TANF; zero out the modeled + # amount, as an API user who knows actual receipt would. + tanf: 0 households: household: members: [head, spouse, child1, child2, child3, child4] state_code: OK output: - # Family is not enrolled in TANF (is_tanf_enrolled defaults to false), - # so the credit applies even though the model would impute TANF - # eligibility. 6 exemptions * $40 = $240. See policyengine-taxsim#988. + # 6 exemptions * $40 = $240. See policyengine-taxsim#988. ok_stc: 240 # 2025 Sales Tax Credit Tests