Skip to content

Implement Indiana Child Care Assistance Program (CCAP / CCDF)#8610

Open
hua7450 wants to merge 6 commits into
PolicyEngine:mainfrom
hua7450:in-ccap
Open

Implement Indiana Child Care Assistance Program (CCAP / CCDF)#8610
hua7450 wants to merge 6 commits into
PolicyEngine:mainfrom
hua7450:in-ccap

Conversation

@hua7450

@hua7450 hua7450 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the Indiana Child Care and Development Fund (CCDF) Voucher Program, administered by the Family and Social Services Administration (FSSA) Office of Early Childhood and Out-of-School Learning (OECOSL). The program is the new gov/states/in/fssa/ccdf subtree and flows into the federal child_care_subsidies aggregate and the household-level household_state_benefits registry.

Note: in is a Python reserved keyword, so formula parameter access uses bracket indexing (parameters(period).gov.states["in"].fssa.ccdf...), while dotted adds-path strings still resolve.

Closes #8609

Regulatory Authority

Eligibility

Requirement Source How modeled
Resident of Indiana PM §1.5 (#page=11) defined_for = StateCode.IN on all top-level variables
Eligible child under 13 at application PM §1.6 (#page=12) child_age_limit = 13; in_ccdf_eligible_child checks age < limit
Special-needs child may continue past 13 (to 19) PM §1.6 (#page=12) disabled_child_age_limit = 19; is_disabled extends the age cap. Court-ordered supervision not tracked at the moment
Child must be US citizen or qualified alien PM §1.6 (#page=12) Reuses federal is_ccdf_immigration_eligible_child (only the child's status matters)
Both adults have a qualifying service need, no minimum hours PM §1.7 (#page=15) in_ccdf_activity_eligible requires all head/spouse adults to be in a qualifying activity; no hours threshold or hours input used
Qualifying activity (employment / self-employment / OJT / job search / education / incapacitation / CPS referral) PM §1.7 (#page=15) in_ccdf_parent_in_eligible_activity: positive wages, nonzero self-employment income (a loss still evidences active self-employment), student status, disability, or is_tanf_enrolled. Employment is documented by proof of income, so the weekly-hours input (default 40) is not used as a qualifier
Initial (get-on) income test ≤135% FPL Income-Get-on PDF; PM §1.1 (#page=5) get_on_fpg_rate (1.27 through Jul 2023 per the OECOSL bulletin, 1.50 from Aug 2023, 1.35 from 2026-04-05); in_ccdf_income_eligible applicant branch
Continuing (stay-on) income test ≤85% SMI Income-Stay-on PDF; PM §1.1 (#page=5) stay_on_smi_rate = 0.85 × hhs_smi; selected via the is_in_ccdf_enrolled input
Assets > $1,000,000 → ineligible PM §1.8 (#page=31) Indiana-specific assets/limit = 1_000_000; in_ccdf_asset_eligible (federal is_ccdf_asset_eligible is not reused — distinct authority)

The two income tiers are switched by the is_in_ccdf_enrolled boolean input: initial applicants are tested at 135% FPL, enrolled recipients at 85% SMI (the is_tanf_enrolled applicant/recipient pattern).

Income

in_ccdf_countable_income (SPMUnit, MONTH) sums countable gross monthly income via the income/sources.yaml unit: list parameter (PM §1.8, #page=26):

employment_income, self_employment_income, rental_income, social_security, ssi, unemployment_compensation, veterans_benefits, workers_compensation, child_support_received, alimony_income.

Two modeling decisions:

  • tanf is omitted from countable income. The Policy Manual counts TANF cash, but listing the dollar amount reintroduces the CCDF↔TANF circular dependency through the dependent-care deduction (tanf → TANF dependent-care deduction → childcare_expenseschild_care_subsidies → back to CCDF). is_tanf_enrolled is still used as a service-need gate.
  • social_security_disability is not listed separately. In PolicyEngine, social_security is an umbrella variable whose adds already includes the disability, retirement, survivors, and dependents components. Listing both would double-count SSDI (a person with $12,000/yr SSDI produced $2,000/mo gross before this fix, $1,000/mo after).

Earned income of household members under 18 is excluded in the formula.

Copay

in_ccdf_copay (SPMUnit, MONTH) implements the income-based copay (PM §2.6, #page=39 + the 2026 Sliding Fee Schedule):

weekly_copay = max_(countable_income, 0) × fee_factor.calc(fpg_ratio)
monthly_copay = weekly_copay × 52 / 12
  • The variable returns the monthly copay: Indiana's schedule sets a weekly copay, and the weekly→monthly conversion happens inside the variable so the MONTH-defined value is a monthly dollar amount.
  • The fee_factor is a single_amount bracket parameter keyed on income as a ratio of the FPG, with the Year 1-3 (new-enrollee) tier: 0 / 0.0116 / 0.0140 / 0.0163 / 0.0163 / 0.0186 / 0.0209 / 0.0233 / 0.0302 / 0.0372 / 0.0442 across 11 brackets.
  • Copay is $0 at ≤100% FPL (first bracket). The published dollar bands span (prior band's ceiling, own ceiling], so each bracket's lower bound sits just above the prior band's ceiling (+0.0001) — a family at exactly a published boundary (100%, 109%, 118%, 135%, ...) stays in its labeled band.
  • Income is floored at 0 before multiplying so a self-employment loss cannot produce a negative copay.

Reimbursement Rates

Per the scope decision, Indiana's reimbursement-rate cap follows Option B: Marion County (the most populous county, ~977K) FY2023 rates (effective 2023-07-30) are transcribed as a statewide proxy ceiling. Indiana otherwise publishes ~8,932 distinct rates (92 counties × ~16 provider × PTQ rows × 6 age groups × weekly/daily/hourly) with no statewide or cluster file.

  • The rates/full_time.yaml parameter is a breakdown over [in_ccdf_provider_type][in_ccdf_ptq_level][in_ccdf_age_group] using the full-time (weekly) rate, converted to monthly in the benefit formula via × WEEKS_IN_YEAR / MONTHS_IN_YEAR.
  • in_ccdf_provider_type and in_ccdf_ptq_level are Person-level enum inputs; in_ccdf_age_group is a derived Person variable from the child's age via parameterized month cutoffs (Infant <12mo, Toddler <36mo, Three-to-Five <72mo, Kindergarten = age 6, School Age Before/After = age 7+).
  • Special-needs add-on: special_needs_multiplier = 1.10 raises the cap by 10% for children with documented special needs.
  • Provider families with no separate PTQ Level 1 row (every family except Registered Ministry) carry their base value at PTQ_1; families that publish only a base row (Exempt Center, Accredited Exempt Center, Exempt Home) carry the base value across all PTQ levels.
  • Oct 5, 2025 rate cut encoded: OECOSL cut rates effective 2025-10-05 (infants/toddlers −10%, preschoolers −15%, school age including kindergarten −35% per the Sept 4, 2025 press release; effective date per the Sept 29 OECOSL bulletin). The official post-cut dollar tables are only on the Provider Portal, so the 2025-10-05 entries are derived from the FY2023 Marion values (× 0.90 / 0.85 / 0.65 by age group) and can be corrected in place if the official sheets publish.

Sample of the full-time weekly rate table (Marion FY2023, pre-cut era; the 2025-10-05 entries apply the percentage cuts to every cell):

Provider × PTQ Infant Toddler 3-4-5 Kindergarten SA Before/After SA Other
Licensed Center (base) 416 327 231 220 110 218
Licensed Center — PTQ 4 468 374 295 264 155 278
Licensed Home (base) 156 150 143 143 92 143
Registered Ministry (base) 351 277 185 178 73 173
Exempt Center (base) 532 418 274 254 95 239
Exempt Home (base) 143 143 143 143 80 143

The benefit formula is in_ccdf = max_(min_(pre_subsidy_childcare_expenses, max_rate) − monthly_copay, 0): the subsidy is the lesser of provider charge and reimbursement rate, minus the copay, with the family paying any overage above the rate directly. The expense cap pools the per-child maximum rates across the unit (the MA / RI / AK child care subsidy convention) since childcare expenses are a single SPM-unit input.

Not Modeled

What Source Why excluded
Court-ordered supervision (age extension pathway) PM §1.6 (#page=12) We don't track court-ordered supervision status at the moment
Licensed-foster-family income exclusion PM §1.8 (#page=26) We don't track licensed-foster-family status at the moment
CPS-family income exclusion PM §1.8 (#page=26) We don't track documented-CPS-case status at the moment
Lump-sum Social Security & earned-income adjustments (advance pay, insurance, reimbursements, housing/food allowance, employer retirement) PM §1.8 (#page=27) We don't break out these pay-stub-level adjustments at the moment
Nanny care hourly rate (minimum wage ÷ number of children) PM §2.5 (#page=38) We don't track nanny-care provider type at the moment; the standard rate table is used
OMW Pre-K Regular copay waiver PM §2.6 (#page=39) On My Way Pre-K is a separate program not in scope
Not-yet-paid new employment / active job search as a service need PM §1.7 (#page=15) We don't capture employment without income yet or job-search status at the moment
Full per-county rate tables (all 92 counties) Per-county rate sheets Marion County FY2023 rates proxy the state; full tables (~8,932 values) deferred
Provider certification (470 IAC 3-18 fire safety / criminal history) 470 IAC 3-18 We don't model provider certification
Years-on-program / enrollment cohort (fee-factor tiers Year 4-10+) Sliding Fee Schedule (#page=1) We don't track years-on-program at the moment; only the Year 1-3 tier is modeled
Care schedule (School Age Before/After vs All Other) Provider Manual (#page=11) We don't track care schedule at the moment; School Age defaults to Before/After

Modeling Notes / Limitations

  • Statewide rate proxy: Marion County rates proxy all 92 counties. A cross-county check showed rates are largely a statewide cap, but Marion (urban) overstates rural counties somewhat.
  • Full-time care only: Only full-time (weekly) care is modeled; daily and hourly part-time rates are not modeled separately.
  • Copay fee factors: The fee factors are only available from 2026-04-05 (the 2026 Sliding Fee Schedule); the older 2025 schedule returned HTTP 404. Earlier simulation periods backward-extrapolate the 2026 factors, so pre-2026 copays are approximate.
  • Oct 2025 rate cut: encoded as derived percentage cuts on the Marion FY2023 proxy values; the official post-cut dollar tables (Provider Portal only) may round differently.
  • 127% era: The pre-Aug-2023 127% FPL initial limit is encoded at PolicyEngine's 2015 backdating floor; the exact start date of the 127% era is unverified.
  • Kindergarten vs School Age: The Kindergarten / School-Age and Before-After / All-Other splits are enrollment- and schedule-driven, not purely age-driven. We approximate with age cutoffs (K = age 6, School Age = 7+) and don't track half-day-vs-full-day kindergarten enrollment or care schedule at the moment.
  • Cohort tiers: We don't track years-on-program / enrollment cohort at the moment, so only the Year 1-3 fee-factor tier is applied.
  • Boundary-test robustness: The 2027 FPG and 2026 Indiana SMI are forecast/uprated values that shift when actuals publish, so income-eligibility boundary tests keep comfortable margins on each side of the limits rather than pinning exact forecast-derived boundaries.

Test plan

  • 108 tests pass locally (policyengine-core test policyengine_us/tests/policy/baseline/gov/states/in/fssa/ccdf/ -c policyengine_us), including pre- and post-cut rate era cases
  • Multi-agent review (5 finder dimensions + adversarial verification of every finding) and post-fix verification pass
  • CI passes

Files Added

policyengine_us/parameters/gov/states/in/fssa/ccdf/
  age_group/infant_max_months.yaml
  age_group/toddler_max_months.yaml
  age_group/preschool_max_months.yaml
  age_group/kindergarten_max_months.yaml
  assets/limit.yaml
  copay/fee_factor.yaml
  eligibility/child_age_limit.yaml
  eligibility/disabled_child_age_limit.yaml
  income/get_on_fpg_rate.yaml
  income/stay_on_smi_rate.yaml
  income/sources.yaml
  rates/full_time.yaml
  rates/special_needs_multiplier.yaml

policyengine_us/variables/gov/states/in/fssa/ccdf/
  in_ccdf.py
  in_child_care_subsidies.py
  is_in_ccdf_enrolled.py
  copay/in_ccdf_copay.py
  eligibility/in_ccdf_eligible.py
  eligibility/in_ccdf_eligible_child.py
  eligibility/in_ccdf_income_eligible.py
  eligibility/in_ccdf_asset_eligible.py
  eligibility/in_ccdf_activity_eligible.py
  eligibility/in_ccdf_parent_in_eligible_activity.py
  income/in_ccdf_gross_income.py
  income/in_ccdf_countable_income.py
  rates/in_ccdf_age_group.py
  rates/in_ccdf_provider_type.py
  rates/in_ccdf_ptq_level.py
  rates/in_ccdf_max_rate_per_child.py

policyengine_us/tests/policy/baseline/gov/states/in/fssa/ccdf/
  in_ccdf.yaml
  in_child_care_subsidies.yaml
  integration.yaml
  copay/in_ccdf_copay.yaml
  eligibility/in_ccdf_eligible.yaml
  eligibility/in_ccdf_eligible_child.yaml
  eligibility/in_ccdf_income_eligible.yaml
  eligibility/in_ccdf_asset_eligible.yaml
  eligibility/in_ccdf_activity_eligible.yaml
  eligibility/in_ccdf_parent_in_eligible_activity.yaml
  income/in_ccdf_countable_income.yaml
  rates/in_ccdf_age_group.yaml
  rates/in_ccdf_max_rate_per_child.yaml

Shared registration edits:
  policyengine_us/parameters/gov/hhs/ccdf/child_care_subsidy_programs.yaml  (add in_child_care_subsidies to adds — federal child_care_subsidies aggregate)
  policyengine_us/parameters/gov/household/household_state_benefits.yaml  (register in_child_care_subsidies in the 2024 block — household_benefits / household_net_income)
  policyengine_us/programs.yaml  (add IN state_implementations entry under ccdf)

changelog.d/in-ccap.added.md

🤖 Generated with Claude Code

hua7450 and others added 2 commits June 8, 2026 21:31
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ngine#8609)

Indiana FSSA/OECOSL Child Care and Development Fund voucher program:
eligibility (residency, child age, citizenship, both-adult service need,
two-tier income 135% FPL / 85% SMI, $1M assets), countable income,
income-based weekly copay, and Marion-County-proxy reimbursement rates
(provider type x PTQ level x age group). 99 tests passing.

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

codecov Bot commented Jun 9, 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 (de4484e).
⚠️ Report is 26 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##             main     #8610       +/-   ##
============================================
+ Coverage   77.77%   100.00%   +22.22%     
============================================
  Files           1        16       +15     
  Lines           9       232      +223     
============================================
+ Hits            7       232      +225     
+ 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 3 commits June 9, 2026 00:17
- Drop is_tax_unit_dependent gate from eligible-child (Policy Manual §1.6
  defines child by age/custody/relationship, not tax dependency; follows WV/AK)
- Fix stay_on_smi_rate reference (#page=5 -> #page=10) and add OECOSL Jul-2023
  bulletin to get-on/stay-on for the 2023-08-01 expansion
- Add dedicated unit tests for in_ccdf_parent_in_eligible_activity; add 2027-01
  copay case and a multi-child case; document is_disabled/asset-exclusion proxies

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Register in_child_care_subsidies in household_state_benefits.yaml so the
  benefit flows into household_benefits/household_net_income (AR precedent)
- Fix fee_factor bracket thresholds to prior-band-ceiling + 0.0001 so incomes
  at published band boundaries land in their labeled band
- Convert in_ccdf_copay to a monthly amount inside the variable (was a weekly
  value in a MONTH-defined variable); update copay test expectations
- Backdate get_on_fpg_rate with the documented 127% FPL pre-Aug-2023 era
- Replace forecast-pinned income-eligibility boundary tests with values robust
  to FPG/SMI parameter refreshes; assert eligibility in the ineligible case
- Note the Oct 5, 2025 FSSA rate cut as a known limitation in full_time.yaml
- Fix stale/incorrect comments (test case FPL ratios, FPG uprating rationale,
  era-specific income limit) and citation page anchors; collapse duplicate
  same-document references to one link per file
- Drop sources/working_references.md scratch notes from the PR

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hua7450 hua7450 marked this pull request as ready for review June 9, 2026 21:19
- Add 2025-10-05 entries to rates/full_time.yaml derived from the FY2023
  Marion values (infant/toddler -10%, preschool -15%, school age including
  kindergarten -35% per the Sept 4, 2025 OECOSL announcement; the official
  post-cut tables are only on the Provider Portal). Recompute the 2026
  rate-dependent test expectations and add a pre-cut era case.
- Qualify the employment service need on positive wages or nonzero
  self-employment income instead of the weekly-hours input, whose default
  of 40 made the activity test default-true for adults with no hours data.
  Employment is documented by proof of income with no minimum hours
  (PM 1.7); a self-employment loss still evidences active self-employment.

Co-Authored-By: Claude Fable 5 <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.

Implement Indiana Child Care Assistance Program (CCAP / CCDF)

1 participant