Add San Francisco County Adult Assistance Program (CAAP)#8591
Draft
hua7450 wants to merge 4 commits into
Draft
Conversation
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 Report✅ All modified and coverable lines are covered by tests. 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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
Scope
gov/local/ca/sf/caap/, consistent with the sibling CA county GA programs (Alamedagov/local/ca/ala/ga/, Riversidegov/local/ca/riv/general_relief/, LAgov/local/ca/la/general_relief/). Like those siblings,ca_sf_caapis NOT wired intospm_unit_benefits.pyorprograms.yaml— flagging this for the reviewer to confirm the county-GA convention.Income Eligibility & Resource Tests
in_san_franciscolocator (county_str == "SAN_FRANCISCO_COUNTY_CA"); every CAAP variable chainsdefined_forthrough the eligibility variableca_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 momentca_sf_caap_personal_property_eligible(spm_unit_cash_assets <= limit); single/couple limits ineligibility/personal_property/limit/{single,married}.yamlca_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 testca_sf_caap_ineligible_person(ssi > 0); SSI recipients are excluded fromca_sf_caap_budget_unit_sizeand their (exempt) income drops outca_sf_caap_immigration_status_eligibleagainst thequalified_immigration_status.yamlwhitelist (CITIZEN, LEGAL_PERMANENT_RESIDENT, REFUGEE, ASYLEE, CUBAN_HAITIAN_ENTRANT, DEPORTATION_WITHHELD, CONDITIONAL_ENTRANT, PAROLED_ONE_YEAR), reusing the existingimmigration_statusenum. PRUCOL / SF-specific humanitarian categories are approximated by this setIncome 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):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}.yamllists.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:
written as an explicit
base − max_(countable, 0)(not anadds/subtractsaggregation) 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):In-kind value chart (Title 22 §50511, Div 99-1, Manual #page=467), effective 2025-11-01:
(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.
in_san_francisco(+defined_forchain)in_san_francisco.yaml,eligible.yamleligibility/age_threshold.yamlca_sf_caap_age_eligibleca_sf_caap_age_eligible.yaml,eligible.yamleligibility/personal_property/limit/{single,married}.yamlca_sf_caap_personal_property_eligiblepersonal_property_eligible.yamlca_sf_caap_income_eligibleincome_eligible.yamlqualified_immigration_status.yamlca_sf_caap_immigration_status_eligibleimmigration_status_eligible.yamlca_sf_caap_ineligible_person,ca_sf_caap_budget_unit_sizeineligible_person.yaml,budget_unit_size.yaml,ca_sf_caap.yamlca_sf_caap_countable_income,_countable_income_person,_income_in_kindcountable_income.yaml,ca_sf_caap.yamlcountable_income/sources/{earned,unearned}.yamlca_sf_caap_earned_income,_unearned_incomecountable_income.yamlearned_income_disregard.yamlca_sf_caap_earned_income_disregard,_net_earned_income,_is_recipientearned_income_disregard.yaml,net_earned_income.yamlincome_in_kind/{housing,utilities,food,clothing,extra_person}.yamlca_sf_caap_income_in_kind+ 4*_provided_in_kindflagsincome_in_kind.yaml,ca_sf_caap.yamlamount/ga/{by_family_size,extra_person}.yamlca_sf_caap_max_grantca_sf_caap_max_grant.yamlca_sf_caapca_sf_caap.yamlspecial_allowance/floor.yamlca_sf_caapca_sf_caap.yamlamount/ga/by_family_size.yaml(size 2)ca_sf_caap_max_grantca_sf_caap_max_grant.yaml,ca_sf_caap.yamlspm_unit_cash_assets; documented in docstringsincome_in_kind.yamlNot Modeled
We don't track the following at the moment:
Historical Notes
Files Added
Test plan
sf/tree, including the pre-existingsf/wftctests).make format(ruff format) andruff checkclean.