Skip to content

Add San Francisco County Adult Assistance Program (CAAP)#8591

Draft
hua7450 wants to merge 4 commits into
PolicyEngine:mainfrom
hua7450:ca-sf-caap
Draft

Add San Francisco County Adult Assistance Program (CAAP)#8591
hua7450 wants to merge 4 commits into
PolicyEngine:mainfrom
hua7450:ca-sf-caap

Conversation

@hua7450
Copy link
Copy Markdown
Collaborator

@hua7450 hua7450 commented Jun 5, 2026

Summary

Implements the San Francisco County Adult Assistance Programs (CAAP) — General Assistance (GA) tier — in PolicyEngine. CAAP is San Francisco's locally funded cash-assistance program for indigent adults, administered by the SF Human Services Agency under SF Administrative Code Ch. 20 Article VII and CA Welfare & Institutions Code §17000. This PR models the GA grant (fill-the-gap: max grant minus countable income), asset and immigration eligibility, the 5-tier earned-income disregard (recipients only), the Title 22 §50511 in-kind valuation chart with the $59 minimum-cash floor, and SSI-recipient exclusion.

Closes #8590

Regulatory Authority

Citation split: structural / eligibility rules are cited to the amlegal ordinance; dollar values (grant tables, in-kind chart, asset reserve) are cited to the CAAP Manual / Title 22, because the ordinance does not print the rate tables. All #page= anchors on the Manual use the PDF's physical page numbers (the Manual's printed page numbers run ~+424 ahead of physical pages).

Program Overview

  • Administration: County-administered (SF Human Services Agency).
  • Funding: County / local funds, under the CA W&I Code §17000 county GA mandate.
  • Scope of this PR: the GA tier only (single max grant $578). The PAES / CALM / SSIP tier (single max $714) and its activity gates are documented as not modeled (see Scope and Not Modeled).

Scope

  • GA tier ONLY. A single GA max-grant table; no sub-program input variable. The PAES / CALM / SSIP grant tier and the PAES job-readiness, CALM Medi-Cal-linkage, and SSIP disability/pending-SSI activity gates are documented as not modeled.
  • Note placement: this is a county / local program, placed under gov/local/ca/sf/caap/, consistent with the sibling CA county GA programs (Alameda gov/local/ca/ala/ga/, Riverside gov/local/ca/riv/general_relief/, LA gov/local/ca/la/general_relief/). Like those siblings, ca_sf_caap is NOT wired into spm_unit_benefits.py or programs.yaml — flagging this for the reviewer to confirm the county-GA convention.

Income Eligibility & Resource Tests

Test Source How modeled
Resides in San Francisco SEC. 20.7-11 in_san_francisco locator (county_str == "SAN_FRANCISCO_COUNTY_CA"); every CAAP variable chains defined_for through the eligibility variable
Adult, no-dependent-children path SEC. 20.7-6 ca_sf_caap_age_eligible (monthly_age >= 18); a child in the unit fails the test. The CalWORKs-ineligible-parents split is not tracked at the moment
Cash-asset limit $2,000 single / $3,000 couple Title 22 §50420 (SEC. 20.7-13 defers to it) ca_sf_caap_personal_property_eligible (spm_unit_cash_assets <= limit); single/couple limits in eligibility/personal_property/limit/{single,married}.yaml
Net income below the max grant SEC. 20.7-10 ca_sf_caap_income_eligible (ca_sf_caap_countable_income < ca_sf_caap_max_grant, strict <). Uses cash income only — in-kind value is handled at the grant level, not the income test
Not receiving SSI SEC. 20.7-14 ca_sf_caap_ineligible_person (ssi > 0); SSI recipients are excluded from ca_sf_caap_budget_unit_size and their (exempt) income drops out
Qualified immigration status SEC. 20.7-6 ca_sf_caap_immigration_status_eligible against the qualified_immigration_status.yaml whitelist (CITIZEN, LEGAL_PERMANENT_RESIDENT, REFUGEE, ASYLEE, CUBAN_HAITIAN_ENTRANT, DEPORTATION_WITHHELD, CONDITIONAL_ENTRANT, PAROLED_ONE_YEAR), reusing the existing immigration_status enum. PRUCOL / SF-specific humanitarian categories are approximated by this set

Income Deductions & Exemptions

  • 5-tier earned-income disregard (recipients only)ca_sf_caap_earned_income_disregard, a marginal-rate scale on gross monthly earned income (SEC. 20.7-21(j) / Div 94-14):

    Band Disregard Amount disregarded
    First $200 100% $200
    Next $150 ($200–$350) 2/3 $100
    Next $150 ($350–$500) 1/2 $75
    Next $150 ($500–$650) 1/3 $50
    Next $150 ($650–$800) 1/5 $30
    Remainder (> $800) 0% dollar-for-dollar

    The disregard caps at $455. It is gated on ca_sf_caap_is_recipient (a SPMUnit boolean input defaulting to False): applicants receive no disregard.

  • ~9 exempt income types (SEC. 20.7-14: SSI/SSP, tax refunds incl. EITC, foster care / adoption assistance / KinGAP, relocation payments, training/job-readiness payments, student grants/loans paid to the school, locally-funded work-incentive payments, Guaranteed Income Pilot payments, rent grants/loans at application) are modeled by omission from the positive countable_income/sources/{earned,unearned}.yaml lists.

  • In-kind value (Title 22 §50511 chart, below) is deducted from the grant, not from countable income.

Benefit Calculation

Fill-the-gap, computed monthly at the SPM-unit level:

ca_sf_caap_max_grant(family size) − max_(ca_sf_caap_countable_income, 0)

written as an explicit base − max_(countable, 0) (not an adds/subtracts aggregation) so that negative income sources (e.g. self-employment losses) cannot inflate the benefit above the max grant.

The in-kind value of any housing / utilities / food / clothing provided is deducted from the grant (SEC. 20.7-22). When the in-kind value meets or exceeds the grant, the $59 minimum-cash floor (SEC. 20.7-24) tops the otherwise-eligible recipient back up to $59/month. The full grant is treated as received — the Mandatory Direct Rent Payment routing (landlord-direct vs. cash) is not split out, since it does not change the total benefit value.

Income / Grant Standards

GA maximum monthly grant, effective 2024-10-01 (unchanged in both the 2025 and 2026 manuals), from amount/ga/by_family_size.yaml (Div 99.3-1, Manual #page=487):

Family size GA max grant
1 $578
2 $1,036
3 $1,166
4 $1,390
5 $1,583
6 $1,774
7 $1,949
8 $2,127
9 $2,294
10 $2,465
Each person beyond 10 +$35

In-kind value chart (Title 22 §50511, Div 99-1, Manual #page=467), effective 2025-11-01:

Family size Housing Utilities Food Clothing
1 $422 $89 $231 $70
2 $560 $102 $493 $137

(Each person beyond 10 adds $35 to food and clothing. The Manual prints only sizes 1–2 — see Not Modeled.)

Other standards: cash-asset limit $2,000 single / $3,000 couple (Title 22 §50420); $59 minimum cash floor (SEC. 20.7-24); age threshold 18.

Requirements Coverage

18 in-scope requirements: 13 covered, 5 by-design, 0 missing.

REQ Description Param Variable Test
001 Resides in San Francisco in_san_francisco (+ defined_for chain) in_san_francisco.yaml, eligible.yaml
003 Adult / no-dependent-children eligibility/age_threshold.yaml ca_sf_caap_age_eligible ca_sf_caap_age_eligible.yaml, eligible.yaml
004 Cash-asset limit $2k/$3k eligibility/personal_property/limit/{single,married}.yaml ca_sf_caap_personal_property_eligible personal_property_eligible.yaml
007 Net income < max grant ca_sf_caap_income_eligible income_eligible.yaml
008 Immigration whitelist qualified_immigration_status.yaml ca_sf_caap_immigration_status_eligible immigration_status_eligible.yaml
009 Not receiving SSI ca_sf_caap_ineligible_person, ca_sf_caap_budget_unit_size ineligible_person.yaml, budget_unit_size.yaml, ca_sf_caap.yaml
020 Countable = cash − disregard, in-kind to grant ca_sf_caap_countable_income, _countable_income_person, _income_in_kind countable_income.yaml, ca_sf_caap.yaml
021 Counted-income positive list countable_income/sources/{earned,unearned}.yaml ca_sf_caap_earned_income, _unearned_income countable_income.yaml
023 5-tier disregard, recipients only earned_income_disregard.yaml ca_sf_caap_earned_income_disregard, _net_earned_income, _is_recipient earned_income_disregard.yaml, net_earned_income.yaml
024 In-kind chart + $59 floor income_in_kind/{housing,utilities,food,clothing,extra_person}.yaml ca_sf_caap_income_in_kind + 4 *_provided_in_kind flags income_in_kind.yaml, ca_sf_caap.yaml
030 GA max-grant table 1–10 + $35 amount/ga/{by_family_size,extra_person}.yaml ca_sf_caap_max_grant ca_sf_caap_max_grant.yaml
031 Fill-the-gap, floor countable at 0 ca_sf_caap ca_sf_caap.yaml
032 $59 minimum cash floor special_allowance/floor.yaml ca_sf_caap ca_sf_caap.yaml
034 Couple = family size 2 amount/ga/by_family_size.yaml (size 2) ca_sf_caap_max_grant ca_sf_caap_max_grant.yaml, ca_sf_caap.yaml
005 Asset exemptions (burial/life-ins/vehicle) by design excluded upstream by spm_unit_cash_assets; documented in docstrings
022 ~9 exempt income types by design modeled by omission from the positive sources lists
035 No personal-needs allowance by design nothing to add (SEC. 20.7-23)
037 COLA 2025 + 2026 by design grant table unchanged 2025→2026; single 2024-10-01 entry extrapolates forward
In-kind chart sizes 3–10 by design source gap (Manual prints only sizes 1–2); formula clips to size-2 fallback income_in_kind.yaml

Not Modeled

We don't track the following at the moment:

  • PAES / CALM / SSIP tier ($714 single grant) and the PAES job-readiness, CALM Medi-Cal-linkage, and SSIP disability + pending-SSI activity gates (SEC. 20.7-6) — out of scope for this GA-only PR.
  • 15-day (GA/CALM/SSIP) / 30-day (PAES) continuous residency (SEC. 20.7-11) — not simulatable from survey data.
  • Real-property / home test (SEC. 20.7-12) — known framework limitation.
  • ID, finger/photo-imaging, fleeing-felon, CalWORKs time-limit, SUD-treatment gates (SEC. 20.7-16 through 20.7-20) — not simulatable.
  • Mandatory Direct Rent Payment (SEC. 20.7-33) — full grant treated as received; this is a payment-routing detail that does not change the total benefit.
  • Housing supplement / +$50 cash (Div 94-14.1, up to 3×/12 months) — episodic special-needs payment requiring month-over-month income-drop tracking.
  • In-kind chart sizes 3–10 — the Manual prints only sizes 1–2; sizes 3–10 are a genuine source gap, so the formula clips to the size-2 value as a fallback.
  • 2019 / 2020 manual eras — out of scope; only the 2025 + 2026 eras are implemented.

Historical Notes

  • The GA grant amounts effective 2024-10-01 are unchanged across both the 2025 and 2026 manuals, so a single parameter entry covers both eras — no separate COLA parameter is needed (the grant table already is the COLA-adjusted value, so fabricating a COLA series was avoided).
  • The in-kind value chart is dated 2025-11-01 (its manual revision date, Div 99-1 rev. 11/1/25).
  • Structural lists, the $59 floor, the disregard scale, and the asset limits are dated 2017-01-01 (the ordinance operative date, Ord. 153-16) to avoid backward-extrapolation phantom benefits.

Files Added

parameters/gov/local/ca/sf/caap/                    (15 files)
  amount/ga/{by_family_size,extra_person}.yaml
  income_in_kind/{housing,utilities,food,clothing,extra_person}.yaml
  special_allowance/floor.yaml
  earned_income_disregard.yaml
  countable_income/sources/{earned,unearned}.yaml
  eligibility/age_threshold.yaml
  eligibility/personal_property/limit/{single,married}.yaml
  qualified_immigration_status.yaml

variables/gov/local/ca/sf/
  in_san_francisco.py                               (locator)
  caap/                                             (21 files)
    ca_sf_caap.py, ca_sf_caap_max_grant.py, ca_sf_caap_is_recipient.py
    eligibility/  (7: eligible, age, income, personal_property,
                  immigration_status, ineligible_person, budget_unit_size)
    income/       (11: countable_income, countable_income_person,
                  earned_income, net_earned_income, earned_income_disregard,
                  unearned_income, income_in_kind, and 4 *_provided_in_kind flags)

tests/policy/baseline/gov/local/ca/sf/
  in_san_francisco.yaml
  caap/  (14: max_grant, ca_sf_caap, integration, + eligibility/ and income/ trees)

Test plan

  • 81 CAAP YAML tests pass (87 across the full sf/ tree, including the pre-existing sf/wftc tests).
  • make format (ruff format) and ruff check clean.
  • CI passes.

hua7450 and others added 2 commits June 5, 2026 10:12
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ier (ref PolicyEngine#8590)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (fbb907a) to head (27ae9d6).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main     #8591    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            3        22    +19     
  Lines           68       247   +179     
==========================================
+ Hits            68       247   +179     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

hua7450 and others added 2 commits June 5, 2026 11:47
- age_threshold: re-cite to CAAP Manual Div 91-4.5 Age (#page=144)
- qualified_immigration_status: re-cite to Div 91-4 Citizenship/PRUCOL (#page=123/125)
- unearned income sources: repoint #page=316 (earned) to Div 92-1 Unearned (#page=190)
- add size-11 in-kind and disregard 1/5-tier coverage tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ca_sf_caap: cap the SEC. 20.7-24 $59 floor at grant_after_income so
  in-kind support can no longer increase the net benefit (over-pay fix)
- add floor boundary tests (over-pay guard, genuine floor, cash_grant==59)
- drop "Div" prefix in param reference titles to match manual labeling
- repoint unearned income sources to Manual 92-40 (#page=229)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add San Francisco County Adult Assistance Program (CAAP)

1 participant