Implement Arkansas School Readiness Assistance (SRA / formerly CCAP)#8324
Draft
hua7450 wants to merge 15 commits into
Draft
Implement Arkansas School Readiness Assistance (SRA / formerly CCAP)#8324hua7450 wants to merge 15 commits into
hua7450 wants to merge 15 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8324 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 3 20 +17
Lines 63 324 +261
Branches 0 1 +1
==========================================
+ Hits 63 324 +261
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- Delete reinvented is_ar_sra_asset_eligible; delegate to federal is_ccdf_asset_eligible - Add ar_child_care_subsidies to gov/hhs/ccdf/child_care_subsidy_programs.yaml - Register AR SRA in programs.yaml under CCDF state implementations - Align in_effect.yaml start date to 2025-11-01 to match published rate sheets (Oct 2025 had different transitional rates and is documented as not-modeled)
…st.md - Delete orphan parameter income/excluded_sources.yaml (never referenced). - Rename was_tea_recipient/months_since_tea_exit → ar-prefixed for namespace. - LI activity: use age>=18 (not is_tax_unit_head_or_spouse); OR in meets_ccdf_activity_test fallback for unmodeled activities. - ESS activity: mask weekly_hours_worked to adults so working teens don't satisfy the threshold. - ar_sra: read pre_subsidy_childcare_expenses at period (auto-divides annual→monthly); drop redundant manual /MONTHS_IN_YEAR. - Compare monthly income vs monthly SMI in is_ar_sra_income_eligible and ar_sra_income_tier (single conversion instead of annualization). - Sweep parameter descriptions to use allowed verbs (limits / provides / sets / excludes / deducts / uses). - Remove absolute_error_margin from boolean-output test files. All 80 AR SRA tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tier 1 — Honest references (drop or reframe phantom citations): - age_category_months.yaml: replace FSU §4.1 (doesn't define month boundaries) with the rate sheet (establishes the age-category labels) and strengthen the inline note that month boundaries are peer-state convention, not AR source. - full_time_hours_threshold.yaml: keep rate-sheet citations but retitle to make clear they establish FT/PT as billing categories — the 30-hour cutoff itself is peer-state convention. - child_age_limit.yaml, is_ar_sra_age_eligible.py, is_ar_sra_child_eligible.py: drop FSU co-citation (FSU §4.1 doesn't state the age-13 rule; only Title 016 §3.1.4 does). The two .py files also disagreed on which FSU page (13 vs 14). - base_rate.yaml: add top-of-file note that PT NIGHT_WEEKEND cells mirror PT REGULAR as a fallback — the source rate sheets have no PT N/W row. Tier 3 — Polish: - ar_sra.py: add clarifying comments on the YEAR→MONTH conversions for pre_subsidy_childcare_expenses (auto-divides at MONTH period) and childcare_attending_days_per_month (read at this_year to preserve count semantics). - Standardize absolute_error_margin to 0.1 across all currency rate tests (was 0.01, inconsistent with project convention of 0.1). - Expand variable-file reference tuples to cite all 4 rate-sheet PDFs (Statewide FT/PT + Benton-Washington FT/PT) — variables previously cited a subset of what the YAML cited. 80/80 AR SRA tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Revert 4-PDF reference expansion in 4 rate variable files (ar_sra_daily_base_rate / daily_copay / daily_state_payment / state_share). Each variable does a parameter lookup; the YAML files already cite all 4 rate sheets as the authoritative source. Restore a single canonical URL per variable. - Drop tuple wrapper from ar_sra_care_type.py and ar_sra_age_category.py which had `reference = ( "single_url", )` — string is the convention for a single reference. - Trim no_copay_smi_threshold.yaml and partial_subsidy_smi_threshold.yaml from 2 refs to 1 — the income-tier headers (40%, 60%) appear identically on the FT and PT rate sheets so a second citation is redundant. 80/80 AR SRA tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add 17 test cases targeting BLOCKER and HIGH-priority gaps from the round-3 review pass: integration.yaml (6 new cases): - Case 9: SN2 special-needs care exercises non-REGULAR care type path - Case 10: low monthly expense ($300) binds before the state payment cap - Case 11: mixed-eligibility household (one 4-year-old, one 13-year-old) confirms per-child masking in the spm_unit.sum - Case 12: zero attending days produces $0 benefit despite eligibility - Case 13: school-aged child at LE_40 SMI confirms 80% state share flows through to a non-zero benefit (always-copay rule) - Case 14: part-time infant care exercises the PT category path ar_sra_li_activity_eligible.yaml (2 new cases): - Case 6: meets_ccdf_activity_test fallback for unmodeled activities - Case 7: mixed work + full-time-student adult exemption ar_sra_ess_eligible.yaml (4 new cases): - Case 6: month 12 is last Year-1 month, 20 hr threshold satisfied - Case 7: month 24 is last in-window month, 25 hr threshold satisfied - Case 8: month 25 is past the window, household not eligible - Case 9: working teen does not satisfy threshold when adult masked hours fall below — exercises the adult-only hours masking ar_sra_income_tier.yaml (4 new cases): - Cases 4-5: 40% SMI boundary (LE_40 just under, GT_40_LE_60 just over) - Cases 6-7: 60% SMI boundary (GT_40_LE_60 just under, GT_60 just over) ar_sra_eligible.yaml (1 new case): - Case 6: high assets ($2M) above CCDF asset limit fails eligibility Total: 97/97 AR SRA tests pass (was 80/80). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… should) Critical: - Add 4% SPM-unit copay cap (CCDF State Plan §3.1.1; AR self-cert under federal 7%). New max_copay_share_of_gross_income parameter; ar_sra.py adds cap_savings to subsidy when rate-sheet copays exceed the ceiling. Updated integration Cases 1/2/5/13 with cap-adjusted values; added multi-child cap-binding Case 18. - Parameterize adult age threshold (18) via new eligibility/adult_age_threshold.yaml; removed hardcoded 18 from ar_sra_countable_income, is_ar_sra_ess_eligible, is_ar_sra_li_activity_eligible. - Register ar_child_care_subsidies in household_state_benefits.yaml. - Add ESS boundary + below-threshold tests (month 13 with 20/25 hr, 19/24 hr below-threshold). - Cover all 24 state_share keys (added Cases 5-24 covering INFANT and PART_TIME branches). - Add is_ar_sra_eligible: false to integration Case 8 (non-AR). - Add NIGHT_WEEKEND / SN1 / SN3 integration cases (REQ-021). Should-Address: - FT threshold corrected to 35 hr/wk (≈ 5 × 7 hr/day per FSU §5.4.6). - ESS Year-2 activity adds is_full_time_student OR-clause; Year-1 TEA-income-ineligible alt path documented as unmodeled. - Strip trailing zeros across rates YAMLs; remove empty edge_cases dir; add #page=1 anchor on OEC announcement; remove redundant ACF reference. - Rewrite is_ar_sra_eligible scalar return as AND-chain; trim verbose comments in ar_sra_age_category and ar_sra_zone. - Test gaps: zero-adult LI case, age-category month boundaries (17/18, 35/36, 71/72), PT NIGHT_WEEKEND fallback, BW SN2 + NIGHT_WEEKEND, new ar_sra_care_type test file. 140/140 AR SRA tests pass; household_state_benefits tests unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Critical: 0 (none introduced). Should-Address: - Tighten student OR-clause from is_full_time_student to is_full_time_college_student (FSU §4.1.5.4 converts via post-secondary credit hours, not K-12 enrollment). Applies to both LI and ESS tracks. - ESS Year-1 student OR-clause was asymmetrically missing — make Year-1 symmetric with Year-2 (FSU §4.1.5.2 / R&R Nov 2025 both authorize full-time school as Year-1 path). - Restore is_ar_sra_eligible early-return guard (project standard; matches CT/MT/SC/UT pattern, broadcast-safe with YEAR-period in_effect lookup). - ar_sra.py: clamp countable income at 0 before computing copay ceiling (prevents cap_savings inflation on negative income). Add min_(total_uncapped_subsidy + cap_savings, total_expense) clamp so total subsidy never exceeds actual childcare cost. - in_effect.yaml: change from period: year to period: month so the Nov 1, 2025 start date isn't backdated to Jan 1, 2025 by year-period lookup. Drop duplicate Case 3 from in_effect tests. - adult_age_threshold.yaml: cite FSU §4.3 (p.19) which contains the explicit "age eighteen (18) years and over" language (was §4.3.2). - full_time_hours_threshold.yaml: cite FSU §5.4.6 Authorization Care Types table (p.30) rather than the section header (p.29). - ar_sra_child_eligible.yaml: fix header that claimed "tax-unit dependent" check (formula doesn't check it). - Add tests: ESS Year-1/Year-2 student satisfies activity (Cases 14/15), ESS months_since=0 boundary (Case 16), exact 40%/60% SMI tier boundaries (Cases 8/9), zero-expense integration clamp guard (Case 19), negative-income regression guard (Case 20). 146 AR SRA tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Spell out "Employment-Sponsored Subsidy" instead of "ESS" acronym in 3 parameter descriptions (ess_window_months, activity_hours_ess_year_1, activity_hours_ess_year_2). - Simplify hhs_smi access in is_ar_sra_income_eligible and ar_sra_income_tier: drop the explicit period.this_year + /MONTHS_IN_YEAR pattern in favor of reading the YEAR-defined hhs_smi at MONTH period (core auto-converts the USD flow variable annual->monthly). 146 AR SRA tests pass; all critical and should-address findings from multi-round /review-program now cleared. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch FT classification to per-day (FSU §5.4.6 literal): - ar_sra_time_category now reads childcare_hours_per_day; threshold = 7. - Updates ~50 test inputs from childcare_hours_per_week to childcare_hours_per_day. - Fixes the 4-day × 8-hour case that was misclassified as PT under the per-week proxy. Reference / citation fixes: - ess_window_months page anchor 26 → 27 (where 24-month value displays). - adult_age_threshold title: §4.3 "Countable Income" → §4.3 "Income". - full_time_hours_threshold title: §5.4.6 "Authorization Care Types" → "Level of Care Authorized". - income_smi_rate page anchor 22 → 21 (where section header appears). - in_effect: drop #page=1 from the one-page OEC Announcement. Code style (drop multi-line "what" comments, add explanatory NOTEs): - ar_sra.py: trim CCDF Plan and clamp comments; add NOTE on the final expense clamp. - ar_sra_zone, is_ar_sra_ess_eligible, is_ar_sra_li_activity_eligible, ar_sra_countable_income: trim multi-line comments per code-style skill. - is_ar_sra_income_eligible, ar_sra_income_tier: add hhs_smi auto-divide NOTE. Move hard-coded Benton/Washington counties to a list parameter: - New rates/benton_washington_counties.yaml; ar_sra_zone uses np.isin lookup. Test additions / fixes: - $1M asset boundary tests (exact, +$1, -$1) added to ar_sra_eligible.yaml. - Fold ar_sra_in_effect.yaml into ar_sra_eligible.yaml; remove the standalone file. - Reconcile SMI documentation across integration.yaml and ar_sra_state_share.yaml (use uprated $95,201.95 to match hhs_smi at 2026-01-01). - Add just-below 85% SMI boundary case to ar_sra_income_eligible.yaml. - Clarify ESS Case 16 (months_since_tea_exit = 0 = still on TEA, not yet in window). - absolute_error_margin: 0.1 → 0.01 (currency tests); strip 0.1 from Enum-output tier file (no margin needed); rate-output state_share keeps 0.001. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t path, Night/Weekend coercion
Substantive behavior changes (4 review-finding items resolved):
1. **4% cap base switched to gross income** (CCDF State Plan §3.1.1 literal):
- New `ar_sra_gross_income` mirrors `ar_sra_countable_income` but does NOT
exclude children's SSI / Social Security (the FSU §4.3.2 exclusion is
for the countable-income tier, not the per-family copay ceiling).
- `ar_sra.py` uses `ar_sra_gross_income` as the 4% cap denominator.
2. **Age-13+ disability extension** (CCDF State Plan §2.2.1):
- New `child_age_limit_disabled = 18` parameter.
- `is_ar_sra_age_eligible.py` uses the higher limit when `is_disabled`.
- 3 new test cases (disabled child 15 eligible, non-disabled 15 not, age 18 boundary).
3. **ESS Year-1 TEA-income-ineligibility alt path** (FSU §4.1.5.1):
- `is_ar_sra_ess_eligible.py` Year-1 activity OR-clauses `~ar_tea_income_eligible`.
- Year-2 unchanged (FSU §4.1.5.2 does not have the alt path).
- 3 new test cases (Y1 alt path triggers, Y1 work-hours fallback fails when
TEA-income-eligible, Y2 does not include alt path).
4. **Night/Weekend rates coerced to FT-only** (OEC Oct 7 2025 Provider Call):
- Per OEC, Night/Weekend is a separate 5th service category priced at 110%
of FT typical rates — not a FT/PT subdimension.
- `ar_sra_daily_base_rate.py` overrides time-category lookup to FT when
care_type is NIGHT_WEEKEND.
- Test Case 14 (PT NIGHT_WEEKEND infant) expected value updated from
$18 (old PT REGULAR fallback) to $39.60 (Night/Weekend FT rate).
Source-research updates:
- `age_category_months.yaml` reference updated from peer-state convention
flag to AR Minimum Licensing Requirements (DCCECE 2020) for Infant/Toddler
boundaries plus Ark. Code 6-18-207 for kindergarten/School-Age boundary.
- `is_ar_sra_age_eligible.py` reference expanded to include CCDF State Plan
§2.2.1 (disability extension authority).
- `ar_sra_daily_base_rate.py` reference expanded to include OEC Provider Call
10.7.25 (Night/Weekend category definition).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the 3 remaining "Verification TODO" items from the PR description. No formula or parameter-value changes — research-only round. - ar_sra_care_type.py: add FSU Procedural Manual §5.6.1 page-32 reference and a 4-line comment documenting SN1/SN2/SN3 criteria. SN level is set by a licensed medical practitioner's documentation per FSU §5.6.1; not derivable from any current PolicyEngine input. Variable remains a bare Enum input. - activity_hours_li.yaml: 2-line comment clarifying the 30 hr/wk applies to LI track only (ESS year-1 and year-2 thresholds live in separate parameter files). Verified separately (no change needed): - state_share_by_tier.yaml SCHOOL_AGED + LE_40 already gives 0.8/0.7 state share (not 1.0), matching OEC Oct 1 2025 deck's "$4.65 copay on $15.50 rate". No school-aged $0-copay bug. - base_rate.yaml already reflects Oct 1 2025 SN simplification: SN1 = REGULAR; SN2 = SN3 = REGULAR x 1.4 across all zones/ages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- child_age_limit_disabled: 18 -> 19 per FSU §5.6.1 ("under nineteen")
and CCDF State Plan §2.2.1 ("below age 19").
- Night/Weekend rate: populate PT N/W cells as PT REGULAR * 1.10 per
Provider Call slide 4 ("+10% on typical rates"); remove FT-coercion
override in ar_sra_daily_base_rate formula that doubled the PT rate.
- income_smi_rate reference: #page=21 -> #page=22 (§2.2.4 header location).
- Promote dese.ade.arkansas.gov CCDF State Plan as primary source for
child_age_limit{,_disabled} (drop publichealthlawcenter.org aggregator).
- Add ar_sra unit test (5 cases): baseline, total_expense clamp,
zero-expense clamp, 4% gross-income cap binding, negative-income clamp.
- Add ar_sra_gross_income unit test (4 cases): adult-only, child SSI
inclusion (vs countable exclusion), child SSDI inclusion, defined_for guard.
- Repair pre-existing ESS Year-1 alt-path test: use
employment_income_before_lsr (the variable tanf_gross_earned_income
actually reads), not employment_income, so TEA-income-ineligibility
is correctly triggered.
Co-Authored-By: Claude Opus 4.7 (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 Arkansas's School Readiness Assistance (SRA) program (formerly CCAP, transferred from AR DHS to AR Department of Education / Office of Early Childhood in June 2025; SRA-era rates effective Nov 1, 2025). Covers both Low-Income (LI) and Employment-Sponsored Subsidy (ESS) eligibility tracks with the full four-dimensional provider rate table (zone × age × care type × full-time/part-time), enforces the 4% per-family copay ceiling from the CCDF State Plan, and includes 180+ tests covering rate-sheet cells, state-share keys, eligibility tracks, and edge cases.
Closes #8323.
Regulatory Authority
Program Overview
in_effectgate). CCAP-era (pre-2025-11-01) deferred to a follow-up PR — the rate and copay structures differ materially (QRIS-based copay vs income-tier × age-group copay, county Rate Cap vs two-zone flat rates). The Oct 2025 transitional month (Quality Enhancement Payment supplement) is also out of scope.Eligibility
is_ar_sra_age_eligiblereadsis_disabledand selectschild_age_limit(13) orchild_age_limit_disabled(19)is_ccdf_immigration_eligible_childis_ar_sra_income_eligible, parameligibility/income_smi_rate=0.85is_ccdf_asset_eligible(gov.hhs.ccdf.asset_limitis $1M from 2015, matching AR's specification)defined_for = StateCode.AR+ar_sra_zonestate guardis_ar_sra_li_activity_eligible, paramactivity_hours_li=30is_ar_sra_ess_eligible; the alt path checksar_tea_countable_earned_income > tea_income_limit(earnings-only per "earnings alone" wording)activity_hours_ess_year_2=25,ess_window_months=24Benefit Calculation
The state pays a daily rate by zone × age category × care type × full-time/part-time, scaled by an income-tier state share. The family's per-child daily copay equals
base_rate × (1 − state_share). The SPM-unit monthly subsidy is computed assum(min(pre_subsidy_childcare_expenses − copay, max_monthly_state_payment))across eligible children, then increased bycap_savingswhen the total payable copay exceeds 4% of monthly countable income.4% per-family copay ceiling (CCDF State Plan §3.1.1): the State Plan's literal "gross income" wording is reconciled against FSU §4.3.3 (p20), which unconditionally excludes children's SSI and Social Security from the budget. The cap base is therefore
ar_sra_countable_income— adult income only. Per child, the rate-sheet copay used in the cap calculation is clamped to that child's actual provider charge (min(rate_sheet_copay, monthly_expense)), so a child whose expense falls below the scheduled copay does not inflatecap_savings.rates/benton_washington_counties.yaml).childcare_hours_per_day.ESS Track Modeling
Mirrors the California stage-3 pattern using two bare-input variables on the SPM unit:
ar_was_tea_recipient(YEAR, bool, defaultFalse)ar_months_since_tea_exit(MONTH, int, default0)ESS Year 1 = months_since_tea_exit ∈ [1, 12]; ESS Year 2 = (12, 24]. Activity-hour thresholds and the 24-month window are parameter-driven. Hours are masked to adults (
age >= adult_age_threshold, parameterized at 18) before the threshold check.Year-1 activity is satisfied by 20+ hr/wk, any adult being a full-time student, OR family earned income alone exceeding the TEA income limit. The earned-income-only gate follows FSU §4.1.5.1's "earnings alone cause the family to be income ineligible for TEA" wording — unearned income (pension, Social Security, child support) is excluded from this specific test. Implementation:
ar_tea_countable_earned_income > parameters.gov.states.ar.dhs.tea.income.income_limit.Year-2 activity is satisfied by 25+ hr/wk or full-time student (no alt path per FSU §4.1.5.2).
Source-Document Reconciliation
The 4% copay ceiling and the ESS Year-1 alt path both required reconciling two source documents:
4% cap base — "gross income" vs FSU §4.3.3: CCDF State Plan §3.1.1 caps the family copay at "4% of gross income," but FSU §4.3.3 (p20) unconditionally excludes children's SSI and Social Security from the budget. The model uses
ar_sra_countable_income(adult income only) as the cap base, treating the State Plan's "gross" as a colloquial reference to the post-§4.3.3 budget. The FSU Glossary defines "Gross Monthly Income" as "total earned and unearned income before tax deductions unless excluded" (p43), supporting this reading.Year-1 alt path — "earnings alone" vs
~ar_tea_income_eligible: FSU §4.1.5.1 says the family qualifies if "the earnings alone cause the family to be income ineligible for TEA (the family's net countable income exceeds $513 per month)." The "alone" qualifier constrains the gate input to earned income — using the full TEA countable income (which includes pension, Social Security, etc.) would defeat the work-transition purpose of the ESS alt path. The model gates onar_tea_countable_earned_income > tea_income_limit.75% SMI no-cost waiver — State Plan §2.3.1(b) vs Nov 2025 rate sheets: State Plan §2.3.1(b) (p29) commits "no cost" assistance for families under 75% SMI, but the Nov 2025 operational rate sheets only zero the copay below 40% SMI. The model follows the rate sheets because they are the operational document the agency uses to compute copays; the broader State Plan waiver is not modeled (documented in
no_copay_smi_threshold.yamlandar_sra_daily_copay.py).Not Modeled (by design)
Test Plan
policyengine-core test policyengine_us/tests/policy/baseline/gov/states/ar/ade/oec/sra -c policyengine_uscbo_household_income.yamltests pass (no regression fromhousehold_state_benefits.yamlchange)make formatclean (ruff format + ruff check pass)