Plan: CMS-416 EPSDT Pipeline (Issue #380)
On this page
Status
| Step | Description | Status |
|---|---|---|
1 |
canopy-medicaid schema. New migration |
Not started |
2 |
canopy-medicaid store + API. New |
Not started |
3 |
canopy-reporting CMS-416 rewrite. Replace lines 269-349 in |
Not started |
4 |
Tests + docs. Unit tests on age-bucket boundary cases (1, 2, 3-5, 6-9, 10-14, 15-18, 19-20), zero rows produces zero-CSV, multiple referral paths roll up correctly. CSV format matches the CMS template exactly. Update Service Catalog. CHANGELOG |
Not started |
5 |
OpenAPI sync. |
Not started |
6 |
Document HEDIS gap. New section in |
Not started |
Issue: #380
Branch: feat/cms-416-epsdt-pipeline
Labels: type::feature, priority::medium, service::reporting, service::medicaid, program::medicaid, federal-partner::cms, workflow::ready
Context
CMS-416 is the annual EPSDT (Early and Periodic Screening, Diagnostic, and Treatment) participation report — children’s preventive care under Medicaid. States must submit by April 1 for the prior federal fiscal year. Today services/canopy-reporting/src/reporting/medicaid.rs:269-349 is a stub: eligible_for_screening is hardcoded at line 332, screening counts come from nowhere, and no screening data exists in any canopy DB.
This plan adds the table, the ingest endpoint, and a real aggregator. The upstream HEDIS bridge (where actual screening events arrive from external pediatric systems) stays out of scope; ingest is via HTTP POST and can be driven manually for UAT or wired to a future external feed.
Code references
-
services/canopy-reporting/src/reporting/medicaid.rs:269-349— stub region. -
services/canopy-reporting/src/reporting/medicaid.rs:332— hardcodedeligible_for_screening. -
services/canopy-medicaid/migrations/— directory to extend. -
ADR-001 — screenings live in canopy-medicaid.
Scope
In scope:
-
epsdt_screeningstable + CRUD endpoints in canopy-medicaid. -
CMS-416 aggregator rewrite in canopy-reporting.
-
Age-bucket boundary tests.
Out of scope:
-
External HEDIS / pediatric-system bridge.
-
CMS-416 amendment / corrections workflow.
-
Periodicity-schedule encoding (when each child should be screened) — that’s downstream of this plan.
-
EPSDT-specific notice generation when a child is overdue — separate.
Design
epsdt_screenings schema:
CREATE TABLE epsdt_screenings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
child_person_id UUID NOT NULL,
screening_type TEXT NOT NULL,
screened_at TIMESTAMPTZ NOT NULL,
referred BOOLEAN NOT NULL DEFAULT false,
referred_for_treatment BOOLEAN NOT NULL DEFAULT false,
provider_id TEXT,
ffy TEXT NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX epsdt_screenings_by_ffy
ON epsdt_screenings (ffy, screening_type);
Age-bucket aggregator (sketch):
fn age_bucket(age: u32) -> Option<&'static str> {
match age {
0..=1 => Some("0-1"),
2 => Some("2"),
3..=5 => Some("3-5"),
6..=9 => Some("6-9"),
10..=14 => Some("10-14"),
15..=18 => Some("15-18"),
19..=20 => Some("19-20"),
_ => None,
}
}
The aggregator joins epsdt_screenings against canopy-persons' birthdate via the existing person lookup (mirrors how T-MSIS already pulls demographic context).
Files Touched
| File | Change |
|---|---|
|
New migration |
|
New store module |
|
New API module |
|
Register routes |
|
Replace lines 269-349 with real aggregation |
|
Age-bucket boundary tests |
|
Regenerated |
|
Regenerated |
Route + table updates |
|
|
|
Verification
-
cargo nextest run -p canopy-medicaid -p canopy-reporting --lib— unit tests pass. -
cargo xtask api-docs— snapshots regenerate clean. -
cargo xtask dev start && cargo nextest run -p canopy-reporting --test cms416_test --run-ignored only— integration test passes. -
Manual smoke: POST 5 screenings (2 children, mixed referral), GET aggregation, confirm age buckets line up.
-
cargo xtask validate— full battery green.
Documentation Updates
-
Service Catalog — canopy-medicaid + canopy-reporting routes + tables
-
CHANGELOG.adoc— entry under== Unreleased/=== Changed -
docs/modules/ROOT/pages/services/canopy-reporting.adoc— CMS-416 path + HEDIS gap -
docs/modules/ROOT/pages/federal-requirements.adoc— CMS-416 row update -
Plan archive: move to
plans/archive/post-merge