case-detail hard-500s on unknown plugin in a user-layer composition override (graceful-degradation gap, ADR-021)
Summary
GET /cases/{household_id} (worker portal case-detail) returns a hard 500 for a worker whenever that worker has a user-layer composition_documents override (surface case_detail) that references a plugin slug not registered via #[canopy_plugin]. The composition loader raises the ADR-021 closed-set UnknownPlugin error and fails the ENTIRE page render, rather than degrading gracefully.
Evidence
canopy-weblog on every such request:case_detail load_composition failed — plugin slug 'X' referenced by composition is not registered→response failed — Status code: 500.- Reproduced deterministically: dashboard
/returns 200 for the same session;/cases/{hh}returns 500 in ~2ms (fails before any downstream call).
Why it matters (production availability)
A single bad user-layer override bricks that user's entire case-detail page. User overrides are writable via the Customize-my-dashboard surface (ADR-024) and Studio (ADR-022). ADR-021's design intent is graceful degradation ('optional services degrade gracefully'; closed-set composition errors). A user-layer section that can't resolve should be skipped/dropped (and logged), not 500 the page.
Two candidate fixes (decide in the MR)
- Read-time (loader): in the case-detail composition loader, skip an unresolvable section from a user (and arguably role) layer rather than erroring; keep hard-fail only for the jurisdiction baseline. Aligns with ADR-021 graceful degradation.
- Write-time (validation): reject
PUT /v1/composition/case_detail/user/meops whose added slug is not in the baseline/registry (case_detail uses RFC 6902 ops, not user_delta_v1, so it bypasses the ADR-024 user_delta slug validation that dashboard surfaces get). 422 on write.- Likely want BOTH: validate on write AND degrade on read (defense in depth).
How it surfaced
tests/e2e/specs/composition-api.spec.ts writes a user/me case_detail override with a fake slug case-header-stub and (historically) never deleted it, 500ing every later case-detail render for that caseworker and failing ~25 case-detail E2E tests order-dependently. The test-isolation half is being fixed separately (cleanup via the now-existing DELETE /user/me route); THIS issue tracks the underlying loader/validation robustness gap.
Relevant files
services/canopy-web/src/api/case_detail.rs(load_composition 500 path)services/canopy-web/src/api/composition.rs(PUT user/me write path;delete_user_meat ~:1083)rulesets/georgia/composition/case_detail.toml(baseline slugs)- ADR-021 (closed-set composition errors / graceful degradation), ADR-024 (user_delta validation for dashboard surfaces)