Skip to content

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

Open
hua7450 wants to merge 17 commits into
PolicyEngine:mainfrom
hua7450:ca-sf-caap
Open

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

Conversation

@hua7450

@hua7450 hua7450 commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

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 grant (fill-the-gap: max grant minus countable income) for both the General Assistance and PAES tiers, 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, per-person exclusion of SSI and CAPI recipients, and a unit-level CalWORKs bar. PAES enrollment is captured by the shared is_in_work_program input.

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 General Assistance and PAES grant tiers (single max $578 GA / $714 PAES). PAES enrollment is captured by the shared is_in_work_program input. The CALM and SSIP grant tiers and all sub-program activity-qualification gates are documented as not modeled (see Scope and Not Modeled).

Scope

  • GA and PAES grant tiers. ca_sf_caap_max_grant selects the PAES table when any CAAP-eligible member is in a work program (is_in_work_program, masked to budget-unit members), else the GA table; the +$35/person-beyond-the-max-charted-size rule (SEC. 20.7-21(e)) is shared, and the max charted family size is a parameter (amount/max_family_size, 10). The CALM and SSIP grant tiers and the activity-qualification gates (PAES job-readiness, CALM Medi-Cal linkage, SSIP disability/pending-SSI) 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/). ca_sf_caap is registered in programs.yaml (status: partial — GA + PAES grant tiers modeled; CALM/SSIP and activity gates not yet), alongside those siblings. Like them, it is not added to the household benefit aggregator (county GA does not flow into the household state-benefit total).

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,couple}.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 on SSI or CAPI (per person) SEC. 20.7-14; Manual 91-2 (#page=34) ca_sf_caap_eligible_person (~(ssi > 0) & ~ca_capi_eligible_person and a qualified immigration status) is summed into ca_sf_caap_budget_unit_size. SSI and CAPI are individual programs, so each recipient drops out of the unit per person — a non-recipient member still qualifies — and their (exempt) income is excluded
Not on CalWORKs (unit) SEC. 20.7-6; Manual 91-2 (#page=34) ca_tanf > 0 checked directly in ca_sf_caap_eligible; CalWORKs is a family grant, so the bar applies at the unit level. RCA / ECA also bar in the manual but have no PolicyEngine variable (not modeled)
Qualified immigration status SEC. 20.7-6; Manual Div 91-4 (#page=123–127) 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, DACA, TPS), reusing the existing immigration_status enum. PRUCOL / SF-specific humanitarian categories are approximated by this set

Income Deductions & Exemptions

  • Counted income — earned income (less the disregard below) plus unearned income (Social Security, unemployment compensation, veterans' benefits, pensions, child support, workers' compensation, railroad retirement), from the positive countable_income/sources/{earned,unearned}.yaml lists. Only the income of persons counted in the budget unit is included.

  • 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 — capped at the cash entitlement (min_(grant_after_income, $59)), so in-kind support never raises the net benefit above what cash income alone leaves. 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

PAES maximum monthly grant (the higher tier, SEC. 20.7-21(b), Manual #page=43), effective 2024-10-01, from amount/paes/by_family_size.yaml — applied when is_in_work_program:

Family size PAES max grant
1 $714
2 $1,172
3 $1,449
4 $1,722
5 $1,964
6 $2,205
7 $2,422
8 $2,640
9 $2,861
10 $3,107
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,couple}.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 on SSI/CAPI (per person); not on CalWORKs (unit) ca_sf_caap_eligible_person, ca_sf_caap_budget_unit_size, ca_sf_caap_eligible ca_sf_caap_eligible_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:

  • CALM and SSIP grant tiers, and the activity-qualification gates for the higher tiers (PAES job-readiness, CALM Medi-Cal linkage, SSIP disability + pending-SSI; SEC. 20.7-6). The PAES grant tier itself IS modeled (via is_in_work_program); what is not modeled is which sub-program a recipient qualifies for and the separate CALM/SSIP grant amounts.
  • 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.
  • Under-18 emancipated-minor exceptions to the age gate (Manual Div 91-4.5 Age, p.144) — the Manual admits some applicants under 18 who are legally married, divorced, widowed, registered domestic partners, or court-emancipated. PolicyEngine has no emancipation input and does not separately model these statuses for minors, so the age gate applies the general 18-and-over rule and this rare exception pathway is not modeled (documented in ca_sf_caap_age_eligible.py).
  • Pre-11/1/25 in-kind chart values — the §50511 in-kind chart we sourced is the "Revised 11/1/25" version; earlier charts are not backdated, so in-kind valuation is modeled from 2025-11-01 onward (in-kind input flags default off).
  • RCA (Refugee Cash Assistance) and ECA (Entrant Cash Assistance) other-aid bars (Manual 91-2) — barred by the manual but have no PolicyEngine variable. CalWORKs and CAPI are modeled (CalWORKs per unit via ca_tanf; CAPI per person via ca_capi_eligible_person, alongside the SSI exclusion).
  • Immigration categories with no ImmigrationStatus enum member — COFA permanent residents (FSM / Marshall Islands / Palau), VAWA self-petitioners, U-visa, T-visa, and SIV holders are listed as eligible by the manual but cannot be expressed with the current enum (documented in ca_sf_caap_immigration_status_eligible.py).
  • California State Disability Insurance (DIB) — counted as unearned income by the manual but has no PolicyEngine variable (social_security_disability is federal SSDI, a different program, and is not substituted).
  • Mid-month grant proration (Manual pp.495–497) — a full monthly grant is computed; partial-month proration is not modeled.
  • 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/                    (17 files)
  amount/{ga,paes}/by_family_size.yaml, amount/{extra_person,max_family_size}.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,couple}.yaml
  qualified_immigration_status.yaml

variables/
  household/demographic/person/is_in_work_program.py  (shared input; also added by PR #8360)
  gov/local/ca/sf/in_san_francisco.py                 (locator)
  gov/local/ca/sf/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, eligible_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

  • 98 CAAP YAML tests pass (104 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

codecov Bot commented Jun 5, 2026

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@             Coverage Diff              @@
##             main     #8591       +/-   ##
============================================
+ Coverage   77.77%   100.00%   +22.22%     
============================================
  Files           1        23       +22     
  Lines           9       260      +251     
============================================
+ Hits            7       260      +253     
+ Misses          2         0        -2     
Flag Coverage Δ
unittests 100.00% <100.00%> (+22.22%) ⬆️

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 12 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>
…stry

- unearned income sources: drop social_security_disability/retirement
  components (the social_security aggregate already adds them) to avoid
  double-counting SSDI/retirement
- move in-kind-exercising tests to 2026-01 so they fall within the in-kind
  chart's 2025-11-01 effective era (no backward-extrapolated future values)
- register ca_sf_caap in programs.yaml (status: partial, GA tier) alongside
  the other CA county General Assistance programs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gate

The CAAP Manual (Div 91-4.5 Age, p.144) admits some under-18 applicants who
are emancipated minors (married/divorced/widowed/domestic-partnered/court-
emancipated). PolicyEngine has no emancipation input, so this rare exception
is documented as an unmodeled pathway rather than partially modeled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion

The $59 floor reference titled SEC. 20.7-24 (ordinance) was linked to the
Manual PDF. Split into two references so each title matches its href: the
amlegal ordinance for SEC. 20.7-24, and the Manual page 47 where the value
appears.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- immigration: add DACA + TPS to the eligibility whitelist (manual lists
  deferred-action/TPS/PRUCOL as eligible); document COFA/VAWA/U/T/SIV
  (no enum member) as not modeled
- countable unearned income: add workers_compensation and railroad_benefits
  (both counted by the manual)
- other-aid bar: new ca_sf_caap_other_aid_eligible bars CalWORKs (ca_tanf)
  and CAPI (ca_capi) recipients; RCA/ECA not modeled (no PE variable)
- citations: extra-person grant -> Manual SEC. 20.7-21(e) ($35; amlegal shows
  a superseded $14); swap dead Westlaw Title 22 links to Cornell LII; add
  #page=47 to the main variable reference
- document CA SDI/DIB and mid-month proration as not modeled

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t param

- remove alimony_income and strike_benefits from countable unearned income:
  the manual treats alimony only as a client-paid deduction (not received
  income) and has no strike-benefit income type (strike is a work-availability
  rule) -- both were generic-template carryover
- rename personal_property/limit/married.yaml -> couple.yaml and p.married ->
  p.couple (label/description already said "couple"); document that units of
  3+ apply the 2-person reserve

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ligible

Replace ca_sf_caap_ineligible_person with a positive ca_sf_caap_eligible_person
(not an SSI recipient and has a qualified immigration status). budget_unit_size
now adds eligible persons directly (dropping the spm_unit_size dependency), and
countable_income keeps eligible persons' income. Behavior is identical
(count(eligible) == spm_unit_size - count(ineligible)).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PAES (Personalized Assisted Employment Services) recipients get a higher grant
than the base General Assistance table (SEC. 20.7-21(b) vs (a); Manual "Maximum
PAES Grant Amount", single $714 ... $3,107 for size 10, verified at the source).
ca_sf_caap_max_grant selects the PAES table when any unit member is in a work
program, captured by the shared is_in_work_program input (byte-identical to
PR PolicyEngine#8360, which also adds it). The +$35/person-beyond-10 rule (SEC. 20.7-21(e))
moves to a tier-neutral amount/extra_person.yaml. CALM/SSIP higher tiers remain
not modeled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CAPI is an individual SSI-type program, so a CAPI-eligible person is now excluded
from the budget unit per-person (in ca_sf_caap_eligible_person, alongside the SSI
exclusion) rather than barring the whole unit -- a non-CAPI member of the same
unit still qualifies. CAPI uses ca_capi_eligible_person (categorical eligibility),
faithful to the manual's "eligible to or a recipient of" bar. The single-condition
ca_sf_caap_other_aid_eligible wrapper is removed; CalWORKs (ca_tanf, a family
grant) is checked directly at the unit level in ca_sf_caap_eligible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tests no longer set the computed variables ssi, ca_tanf, or ca_capi directly --
they drive them from underlying inputs (age 70 -> ssi; parent+child -> CalWORKs;
aged non-qualified-noncitizen -> CAPI), matching the real household-API flow.
Adds a CAPI-isolation case (aged DACA -> eligible_person false) and an explicit
no-eligible-person case (budget unit size 0 -> ca_sf_caap 0); removes the deleted
other_aid_eligible test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hua7450 hua7450 marked this pull request as ready for review June 7, 2026 22:05
hua7450 and others added 2 commits June 7, 2026 18:05
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ax size

- Mask the is_in_work_program check to ca_sf_caap_eligible_person, so an
  ineligible member (SSI/CAPI/immigration, already dropped from the budget unit)
  who is in a work program no longer pushes the eligible unit onto the higher
  PAES table (a one-person GA unit was becoming PAES). Adds a regression test.
- Parameterize the largest charted family size (amount/max_family_size = 10) and
  use it in ca_sf_caap_max_grant and ca_sf_caap_income_in_kind instead of the
  literal 10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hua7450 hua7450 requested a review from PavelMakarchuk June 7, 2026 23:14
- Add interest_income and rental_income to CAAP countable unearned income
  (Manual #page=190 enumerates both; neither is exempt under SEC. 20.7-14,
  which uses a catch-all "all unearned income not specifically exempt" rule)
- Fix TPS immigration reference: split PRUCOL (#page=125) from TPS (#page=133)
- Reframe earned/unearned source citations to note SEC. 20.7-14 lists
  exemptions and income not exempted under it is counted
- Align extra_person.yaml reference title with the Div 99.3-1 table at #page=487
- Add absolute_error_margin to ca_sf_caap_max_grant test cases
- Add tests: isolated unit-level CalWORKs bar, mixed-unit per-person CAPI
  exclusion with income exclusion, and PAES tier flowing end-to-end

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