Skip to content

Implement Ohio Child Care Assistance Program (Publicly Funded Child Care / PFCC)#8718

Draft
hua7450 wants to merge 3 commits into
PolicyEngine:mainfrom
hua7450:oh-ccap
Draft

Implement Ohio Child Care Assistance Program (Publicly Funded Child Care / PFCC)#8718
hua7450 wants to merge 3 commits into
PolicyEngine:mainfrom
hua7450:oh-ccap

Conversation

@hua7450

@hua7450 hua7450 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements Ohio's Child Care Assistance Program — officially Publicly Funded Child Care (PFCC), administered by the Ohio Department of Children & Youth (DCY) (county Departments of Job & Family Services administer eligibility locally). The program was transferred from ODJFS to DCY and its rules were renumbered from 5101:2-16 to 5180:2-16 (eligibility) and 5180:6-1 (provider payment).

Closes #8717.

⚠️ Important modeling decisions

These two decisions materially shape the implementation and should be reviewed before merge.

1. Rescinded core rule / live-citation-only approach

Ohio appears to have rescinded its core substantive eligibility rule (old 5101:2-16-02) and its need-for-care rule (old 5101:2-16-06) without replacing them inside Chapter 5180:2-16. OAC 5180:2-16-02 and 5180:2-16-06 do NOT exist — codes.ohio.gov returns "Number Not Found." The surviving rules (-04, -05, -08) carry dangling cross-references to the nonexistent 5101:2-16-02/-06.

Per maintainer decision, this PR models only provisions authorized by a currently-live source. Citations are sourced as follows, and never reference -02 or -06:

Provision Cited from
Income %FPG thresholds (145/150/300) DCY Procedure Letter 21
Income definition + exclusions OAC 5180:2-16-03
Copayment (incl. ≤100% FPG $0, protective/homeless waiver) OAC 5180:2-16-05
Definitions (age groups, special-needs, school year, homeless) OAC 5180:2-16-01
Child citizenship Federal CCDF (45 CFR 98.20) via is_ccdf_immigration_eligible_child
Provider payment rates + add-ons + county map OAC 5180:6-1-10

2. Qualifying-activity (need-for-care) requirement is NOT modeled

Because the authorizing rule for the qualifying-activity requirement (employment / education / training / job search) was rescinded with no live in-chapter replacement, eligibility here is determined by income + child age + child citizenship only — there is no work / need-for-care gate.

This is a deliberate limitation: it over-grants eligibility relative to real policy (families with no working/active caretaker would be modeled as eligible). It is documented in the oh_ccap_eligible docstring with "we don't model X at the moment" framing.

Regulatory Authority

OAC rule citations use the per-rule codes.ohio.gov landing pages (single-rule HTML, no #page= anchor). #page= anchors are used only for the genuine multi-page PDFs (DCY PL 21, the copay multiplier Appendix A, and the rate Appendix A).

Income Eligibility

Two-tier, %-FPG only — no State Median Income (SMI) cap (Ohio uses 300% FPG where federal CCDF allows up to 85% SMI):

  • Initial (intake): family gross monthly income ≤ 145% FPG.
  • Special-needs intake:150% FPG.
  • Ongoing (exit): once enrolled, remain eligible until income exceeds 300% FPG.

The intake-vs-ongoing distinction is selected by the bare boolean input oh_ccap_enrolled (where(enrolled, ongoing, initial)), matching the NJ CCAP two-tier pattern.

Countable income = gross earned + gross unearned, minus the enumerated exclusions in 5180:2-16-03 (B)–(I): SSI and the earned income of SSI recipients, child support paid, foster/adoption maintenance, SNAP allotments, income tax refunds / EITC, non-recurring lump sums, and minor full-time-student earnings. There is no childcare or work-expense deduction. The SSI-recipient earnings exclusion (-03(F)(8)) is modeled at the person level (a family member's earned income is zeroed when that member receives SSI) — it cannot be expressed via an adds-list omission alone, so countable income is computed by a formula, not a bare adds.

OWF/TANF basic assistance is statutorily countable (-03(H)(3)(j)) but is omitted from the countable-income sources to break the CCAP↔TANF circular dependency (CCAP income → TANF → dependent-care deduction → childcare_expenseschild_care_subsidies → CCAP). TANF-active families are instead made categorically income-eligible via the bare is_tanf_enrolled input (not computed TANF eligibility).

Copayment

Implements the faithful band-max method from 5180:2-16-05(B), not a simplified ratio bracket:

  1. Annualize monthly income (× 12).
  2. Divide by the 100%-FPG annual figure for the family size → family's %FPG.
  3. Round %FPG up to the next 5%.
  4. Band-max monthly income = (rounded% × 100%-FPG annual) ÷ 12.
  5. Weekly copay = band-max monthly income × Appendix A multiplier ÷ weeks-in-fiscal-year, converted to monthly for the MONTH-period benefit.
  • Copay is $0 at ≤100% FPG (explicit waiver, -05(A)(3)).
  • Copay is waived (→ $0) for protective and homeless families (-05(G)). This is a copay waiver only, not an eligibility bypass.

The Appendix A multiplier table runs from 0.0700 (at 105%) through a flat 0.0875 plateau (130–200%) up to 0.2700 (at 300%); income is floored at $0 before computation.

Provider Payment / Rate table

Payment per child = min(county base rate, customary charge) + add-ons, then summed across children and reduced by copay (floored at $0). Customary charge is pre_subsidy_childcare_expenses.

The rate table has 3 county categories × 2 provider types × 5 age groups × 3 time increments:

  • County categories (1 = lowest-cost rural incl. border-state providers; 2 = mid; 3 = metro/high-cost), mapped via an 88-county membership list (Category 1: 13 counties, Category 2: 39, Category 3: 36 — sums to 88).
  • Provider types: CENTER (Centers / Day Camps / Preschool-School-age / ODE programs) and HOME (Licensed Type A/B Homes & In-Home Aides).
  • Age groups: Infant (<18mo), Toddler (18mo–<3yr), Preschool, School-Age, School-Age-Summer — derived from the child's age in months plus a school-year/summer month check (modeled after AZ CCAP).
  • Time increments: Hourly (<10 hrs/wk), Part-time (10–<33 hrs/wk), Full-time (33+ hrs/wk) — derived from childcare_hours_per_week.

Add-ons:

  • SUTQ (Step Up To Quality): Bronze +10%, Silver +15%, Gold +25%.
  • Accredited: +10%. When also SUTQ-rated, the higher of the SUTQ% or the accredited +10% applies.
  • Non-traditional hours: +5%.
  • Special needs: +5%, or 2× the base rate if special accommodations are approved (5180:6-1-09).
  • All add-ons are capped at the customary charge.

Requirements Coverage

27 of 27 in-scope substantive requirements covered, plus the programs.yaml registry entry (REQ-036) — 28/28.

REQ Description Parameter(s) Variable Test
001 Child under 13 eligibility/child_age_limit.yaml oh_ccap_eligible_child oh_ccap_eligible_child 1-2,8
002 Special-needs child under 18 eligibility/special_needs_child_age_limit.yaml oh_ccap_eligible_child oh_ccap_eligible_child 3-5
003 Child citizenship — (is_ccdf_immigration_eligible_child) oh_ccap_eligible_child oh_ccap_eligible_child 6,9
004 Intake ≤145% FPG income/fpl_rate/initial_eligibility.yaml oh_ccap_income_eligible oh_ccap_income_eligible 1-2,7-8
005 Ongoing ≤300% FPG (two-tier) income/fpl_rate/ongoing_eligibility.yaml oh_ccap_income_eligible, oh_ccap_enrolled oh_ccap_income_eligible 4-5,11-12
006 Special-needs ≤150% FPG income/fpl_rate/special_needs_eligibility.yaml oh_ccap_income_eligible oh_ccap_income_eligible 3,9-10
007 Countable income, no deductions income/countable_income/sources.yaml, earned_sources.yaml oh_ccap_countable_income oh_ccap_countable_income (5)
008 SSI + SSI-recipient earnings excluded (person-level formula) oh_ccap_countable_income oh_ccap_countable_income + integration 5
009 TANF cycle break via is_tanf_enrolled (sources omit OWF) oh_ccap_income_eligible oh_ccap_income_eligible 6
014 ≤100% FPG copay $0 copay/fpl_waiver_threshold.yaml oh_ccap_copay oh_ccap_copay 1,8
015 Band-max copay method copay/multiplier.yaml, weeks_in_fiscal_year.yaml oh_ccap_copay oh_ccap_copay 2-3,9
016 Copay multiplier table copay/multiplier.yaml oh_ccap_copay oh_ccap_copay 2-3,9
017 Weeks-in-FY divisor copay/weeks_in_fiscal_year.yaml oh_ccap_copay (used in copay)
018 Protective/homeless copay waiver oh_ccap_copay oh_ccap_copay 4-6 + integration 4
021 min(rate, charge) + add-ons oh_ccap_rate_with_addons, oh_ccap oh_ccap_rate_with_addons 11
022 Full rate table (3×2×5×3) rates/center.yaml, rates/home.yaml oh_ccap_base_rate oh_ccap_base_rate (11)
023 Time increments time_category/hours.yaml oh_ccap_time_category oh_ccap_time_category (6)
024 Derived age group + summer split age_group/months.yaml, school_age_summer/{start,end}_month.yaml oh_ccap_child_age_group oh_ccap_child_age_group (8)
025 SUTQ Bronze/Silver/Gold addons/quality/sutq.yaml oh_ccap_rate_with_addons, oh_ccap_quality_tier oh_ccap_rate_with_addons 1-4
026 Accredited +10%, max of SUTQ/accred addons/accredited.yaml oh_ccap_rate_with_addons, oh_ccap_is_accredited oh_ccap_rate_with_addons 5-6
027 Non-traditional +5% addons/non_traditional_hours.yaml oh_ccap_rate_with_addons, oh_ccap_non_traditional_hours oh_ccap_rate_with_addons 7
028 Special-needs +5% or 2× addons/special_needs.yaml, special_needs_accommodations_multiplier.yaml oh_ccap_rate_with_addons, oh_ccap_has_special_accommodations oh_ccap_rate_with_addons 8-9
029 Add-ons capped at customary charge oh_ccap_rate_with_addons oh_ccap_rate_with_addons 11
030 County→category map (88) geography/category_{1,2,3}_counties.yaml oh_ccap_county_rate_category oh_ccap_county_rate_category (5)
035 oh_child_care_subsidies + CCDF registry oh_child_care_subsidies (integration)
036 programs.yaml entry

Not Modeled (by design)

Dropped — no live citation (authority was in the rescinded -02/-06):

What Why excluded
Qualifying-activity (need-for-care) requirement Authorizing rule rescinded; see modeling decision #2. Over-grants vs real policy.
Inability-to-care (PCSA/physician) exception Moot once activity is not gated; no live rule.
Homeless eligibility bypass (income/activity waiver) No live rule authorizes it — only the copay waiver (-05(G)) survives, which is kept.
Protective/foster eligibility bypass (income/asset waiver) Same — only the copay waiver is kept.
Post-OWF 12-month transitional ≤150% FPG window No live rule; we don't track OWF-exit timing at the moment.
Ohio $1,000,000 asset cap Authority is the rescinded -02(B)(4); the cap is non-binding. The federal is_ccdf_asset_eligible helper is reused instead.

Deferred — operational details outside the modeled annual→monthly pipeline:

What Why excluded
Pay-period conversion (×4.3 / ×2.15 / ×2) PolicyEngine takes annual income and auto-converts annual→monthly; these caseworker pay-stub factors do not change the modeled relationship.
Copay ≤ cost of care / no copay on absent or PD-only weeks The cost cap is implicitly handled by the min(charge, rate) benefit; we don't track weekly attendance.
July-2026 statutory 7%-of-income copay cap Not yet in any rule (legislative analysis only).
Absent days, PD days, $25 registration fee, monthly-vs-per-day billing We don't track per-day attendance / registration at the moment.

Note: SCHOOL_AGE_SUMMER is fully implemented (enum value, rate-table cells, and the summer-month branch in oh_ccap_child_age_group), but the summer branch is untestable under the JAN / whole-year YAML test harness (test periods are restricted to YYYY-01 or YYYY); the age-group tests exercise the school-year case in January.

Verification TODO

  • Re-verify the copay multiplier table and the provider rate cells cell-by-cell against the source PDFs at high DPI (transposition risk on multi-dimensional rate tables).
  • Confirm DCY Procedure Letter 21 page anchor (#page=2 used).
  • Confirm no post-renumbering divergence between the rescinded-rule substance and current operative policy (the live 5180:2-16-02 page is JS-rendered and could not be captured directly; corroborated indirectly via the unchanged 5180:2-16-03).
  • CI passes.

Test plan

  • 98 tests pass locally (policyengine-core test policyengine_us/tests/policy/baseline/gov/states/oh/dcy/ccap/ -c policyengine_us)
  • CI passes

Files Added

policyengine_us/parameters/gov/states/oh/dcy/ccap/
  addons/
    accredited.yaml
    non_traditional_hours.yaml
    special_needs.yaml
    special_needs_accommodations_multiplier.yaml
    quality/sutq.yaml
  age_group/
    months.yaml
    school_age_summer/start_month.yaml
    school_age_summer/end_month.yaml
  copay/
    fpl_waiver_threshold.yaml
    multiplier.yaml
    weeks_in_fiscal_year.yaml
  eligibility/
    child_age_limit.yaml
    special_needs_child_age_limit.yaml
  geography/
    category_1_counties.yaml
    category_2_counties.yaml
    category_3_counties.yaml
  income/
    countable_income/sources.yaml
    countable_income/earned_sources.yaml
    fpl_rate/initial_eligibility.yaml
    fpl_rate/special_needs_eligibility.yaml
    fpl_rate/ongoing_eligibility.yaml
  rates/
    center.yaml
    home.yaml
  time_category/hours.yaml

policyengine_us/variables/gov/states/oh/dcy/ccap/
  oh_ccap.py
  oh_child_care_subsidies.py
  oh_ccap_base_rate.py
  oh_ccap_child_age_group.py
  oh_ccap_copay.py
  oh_ccap_county_rate_category.py
  oh_ccap_has_special_accommodations.py
  oh_ccap_is_accredited.py
  oh_ccap_non_traditional_hours.py
  oh_ccap_provider_type.py
  oh_ccap_quality_tier.py
  oh_ccap_rate_with_addons.py
  oh_ccap_time_category.py
  eligibility/
    oh_ccap_eligible.py
    oh_ccap_eligible_child.py
    oh_ccap_income_eligible.py
    oh_ccap_enrolled.py
    oh_ccap_countable_income.py

policyengine_us/tests/policy/baseline/gov/states/oh/dcy/ccap/
  integration.yaml
  oh_ccap_base_rate.yaml
  oh_ccap_child_age_group.yaml
  oh_ccap_copay.yaml
  oh_ccap_countable_income.yaml
  oh_ccap_county_rate_category.yaml
  oh_ccap_eligible.yaml
  oh_ccap_eligible_child.yaml
  oh_ccap_income_eligible.yaml
  oh_ccap_rate_with_addons.yaml
  oh_ccap_time_category.yaml

Registry edits:
  policyengine_us/parameters/gov/hhs/ccdf/child_care_subsidy_programs.yaml  (added oh_child_care_subsidies)
  policyengine_us/programs.yaml  (added Ohio CCAP under CCDF state_implementations)

changelog.d/oh-ccap.added.md

hua7450 and others added 2 commits June 22, 2026 11:42
Closes PolicyEngine#8717

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

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@            Coverage Diff             @@
##              main     #8718    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            3        18    +15     
  Lines           47       287   +240     
==========================================
+ Hits            47       287   +240     
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.

- Fix special-needs accommodations 2x citation (5180:6-1-10 (G)(2), not 6-1-09)
- Use add() aggregation idiom in oh_ccap.py
- Make copay multiplier band lookup float32-deterministic (epsilon thresholds)
- Add railroad_benefits to countable income (5180:2-16-03(H)(3)(c))

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.

Implement Ohio Child Care Assistance Program (CCAP / Publicly Funded Child Care - PFCC)

1 participant