Worker portal redesign — composability runtime + design-system extraction
Status update 2026-05-21 (canonical sources)
The description below is the original 2026-05-19 scoping for this tracking issue and is preserved for historical context. Stage-2 ADR ratification reshaped several of its assumptions; the canonical current state lives in:
- Plan:
docs/modules/ROOT/pages/plans/worker-portal-redesign.adoc— Status table, Design section, child issue table - ADR-021 — Composability runtime + plugin model — ratified 2026-05-20
- ADR-022 — Composition override storage layering — ratified 2026-05-20
- Group epic &51 — 19 active child issues / weight 64; 2 deferred
What changed since the original scoping below:
- Stage 2 closed at 2 of 3 ADRs, not 3. The originally scoped ADR-023 (promote-PR mechanism) was reframed during drafting and deferred to #507 (unified config backend across canopy domains) — composition isn't a special config concern, it's the 7th instance of an under-generalized pattern across canopy's 7 config domains.
- DB schema: ratified as one unified
composition_documentstable (not three per-layer tables) with polymorphicscope_key. The "Open architectural questions" section below has Question #2 (closed) resolved. - Promote-live-to-baseline: descoped from v1. The original "Open architectural questions" Question #1 (closed) (promote-PR location) is moot; #492 (closed) closed-deferred to #507. Composition v1 ships using the existing filesystem
rulesets/{juris}/composition/pattern; admins promote via their jurisdiction's existing baseline-edit workflow (PR / Salt / manual edit) external to canopy until #507 lands a write-capable backend. - Studio promote modal: descoped from v1 (#500 weight 5 → 3); admin-driven external promote in v1.
- Merge semantics: ratified as RFC 7396 JSON Merge Patch for the system-defaults → baseline overlay, RFC 6902 JSON Patch for DB-backed layer ops (the two layers have different mutation surfaces — baseline-authoring vs Studio-editing).
- Plugin discovery: ratified as compile-time
#[canopy_plugin]macro +linkmedistributed slice with compile-timePlugin.toml↔️ Rust handler validation. - Composition cache: invalidate-on-write, single-replica v1; multi-replica RabbitMQ-fanout invalidation deferred post-UAT.
- 3 open design questions routed to design via the #486 status comment: Plugin Studio (#501) authoring scope in v1; multi-jurisdiction plugin visibility; i18n catalog fallback when user locale isn't shipped.
The "Architectural takeaways" + "Already done in this codebase" + "What the design does NOT cover" + "Out of scope" sections below remain accurate to current scope; the "Proposed implementation order" sections have been superseded by the plan's Steps section + the epic's child issue table.
Context
External design package delivered 2026-05-14 covering the canopy-web worker portal redesign. Artifacts in /tmp/claude-1000/-home-bitskrieg-code-canopy/canopy-design.zip (354 KB):
- 15 interactive HTML design canvases — pan/zoom artboards covering palette revision (already shipped in !295 (merged)), case detail (refined + reframed directions), dashboard system (12-panel kit + 3 jurisdiction compositions), case-detail composability (13 section types × 3 shell strategies × 3 jurisdictions), jurisdiction studio (onboarding + live-mode + promote-PR + plugin dev), customize dashboard, dark mode sweep, panel states (empty / loading / error), ⌘K command palette, audit log (system + case scopes share one primitive), plugin marketplace, polish pass, design system contract, first-impression states, IDP-aware sign-in.
- ~13,700 LOC of
.jsxsource organized by domain (case-detail, dashboard, studio, audit, marketplace, command, polish, design-system, first-impressions, case-comp). HANDOFF.mdwith explicit architectural decisions, sample TOML schemas, plugin manifest spec, suggested 6-phase implementation order (14-22 weeks).- Reference snapshot of current canopy-web under
uploads/canopy-web/(4,122 LOC of Askama templates + supporting JS/CSS).
Architectural takeaways
The design is opinionated and internally consistent. Five load-bearing decisions:
- Composability as core thesis — jurisdictions edit TOML in
rulesets/{jurisdiction}/rather than fork canopy. Three layers (dashboard composition, case-detail composition, identity composition) all follow the same pattern. - Five-layer storage model — user delta → role override → jurisdiction live override → TOML baseline (git) → system defaults; with explicit "promote live → baseline" as a real PR mechanism (requires git-host API integration TBD).
- Generic IDP interface — email-discovery routing, N IDPs with provider type / host / chip styling, local accounts as a toggle, zero-IDP graceful state. Concrete
idp.tomlschema in handoff. - JWS-signed audit + case history as one primitive — aligns with ADR-014 and the canopy-security work already shipped.
- Plugin manifest model —
Plugin.tomldeclares slug + allowed programs + default span + permissions + i18n catalogs. Runtime renders only what jurisdiction TOML references.
Already done in this codebase (pre-design-package)
✅ Palette revision (orchard tokens) — shipped 2026-05-14 via !295 (merged)✅ --orchard-infoCSS token✅ Light / dark / system theme support in canopy-web✅ Case detail with 6 tabs + 30 caseworker actions across SNAP/TANF/Medicaid/CAPS/WIC (program-specific action handlers per #392 (closed), #393 (closed), #394 (closed))✅ Worker-portal cross-program views✅ FTI auditor role + advisory naming✅ ADR-014 audit hash-chain (canopy-security)✅ JWS-signed determinations (ADR-002)✅ Per-jurisdiction theme configuration (canopy-web/src/theme.rs + rulesets/{jurisdiction}/theme.toml)
The design's Phase 1 (palette + --orchard-info) is therefore largely complete; this project starts at Phase 2 (composability runtime).
What the design does NOT cover (per the handoff's own honesty)
- Worker journeys end-to-end (intake → screening → IEVS → determination → authorize → notice). Screens exist; the journey-level UX doesn't.
- Income tab interaction depth — IEVS resolution UX, add/edit/remove income, employer lookup. Section shape designed; interactions are TBD.
- Determination "sign and authorize" ceremony — currently shown as a button. Given ADR-002 (JWS signed determinations), this is the most consequential moment in the worker workflow; needs microspec.
- Notice creation + preview UX. Output goes through Typst (canopy-typst shared crate); the worker-facing compose/preview/send flow isn't designed.
- Applicant portal. Flagged as a separate Dioxus project (ADR-008); out of scope.
- Mobile / tablet. Not in scope for v1.
- Print views. Case file PDF, hearing packet, compliance report — Typst territory, separate surface.
Open architectural questions (RESOLVED / DEFERRED — see Status update at top)
The handoff flags these as engineering decisions:
- Promote-PR mechanism — where does git-host API access live?
canopy-opsservice? Inline in canopy-web? Separate service? → Moot. Promote-PR descoped from v1; deferred to #507 (unified canopy config backend). - DB override storage — single table per layer (user_layouts, role_overrides, jurisdiction_overrides), or one
config_documentstable with a(jurisdiction, layer, key)index? → Resolved by ADR-022: one unifiedcomposition_documentstable with polymorphicscope_key. - Cross-jurisdiction directory service for federated workers? (Probably no for v1.) → Confirmed not v1.
- Plugin marketplace
installscount tracking — self-reported by jurisdictions, or central canopy.dev registry? → Moot until marketplace federation is in v2 scope; tied to #507's unified backend. - Audit retention policy — 7yr for case actions per federal regs; auth events likely 1yr. Per-jurisdiction override? → Resolved by ADR-022 Decision 4: uniform 1-year for override-layer events in v1.
Required pre-work (ADRs) — SUPERSEDED
Before any composability-runtime code lands, three new ADRs should ratify the architectural shift:
ADR-NNN: Composability runtime + plugin model.→ Ratified as ADR-021 (2026-05-20).ADR-NNN: Storage layering for composition overrides.→ Ratified as ADR-022 (2026-05-20).ADR-NNN: Promote-PR mechanism.→ Deferred to #507 (cross-canopy unified config backend ADR).
Proposed implementation order (sliced into MRs) — SUPERSEDED
See the plan's Steps section + the epic's child issue table for current scope. Original proposal preserved below for historical context.
Stage 1 — Design system extraction (1 MR)
Bake Design System.html + foundations.jsx + components.jsx into static/css/canopy-web.css + templates/_primitives/*.html partial library. Outcome: every existing canopy-web template can be rewritten to use these primitives without changing routing or business logic. Low risk, high leverage. Independent of composability work.
Stage 2 — ADR ratification (3 MRs, ADR-only) 3 MRs → 2 MRs shipped, 3rd deferred
Three ADRs above. No runtime code yet.
Stage 3 — Composability runtime + storage layering (3-4 MRs) 3-4 MRs → 3 active MRs, 1 deferred
- DB migrations for
the 3 override layersthe unifiedcomposition_documentstable per ADR-022 - Composition loader (TOML parse + role filter + override merge)
"Promote live → baseline" mechanism (git-host API integration per ADR)→ deferred to #507
Stage 4 — Identity rework (2 MRs)
- Generic IDP loader from
idp.toml(email-discovery routing + OIDC/SAML 2.0 SSO initiation + local-accounts toggle) - IDP-aware sign-in template (port
Sign In.html)
Stage 5 — Core surfaces (4-5 MRs, one per surface)
- Worker dashboard (12-panel kit composition-driven, Askama + htmx + composition runtime)
- Supervisor dashboard (jurisdiction-aware overrides applied)
- Analyst dashboard (panel subset)
- Case detail with 3 shell strategies (scroll / card-grid / tabs) and 13 section types
- Customize my dashboard (worker-level deltas)
Stage 6 — Maintainer surfaces (3 MRs)
- Jurisdiction Studio: onboarding wizard
- Studio: live-mode composer
+ promote modal(promote modal descoped from v1 pending #507) - Studio: plugin developer view (Plugin Studio)
Stage 7 — Polish (2-3 MRs)
- ⌘K command palette
- Audit log (system + case scopes sharing one primitive)
- First-impression states (splash, maintenance, expired, 404)
Out of scope for v1
- Plugin Marketplace — well-designed but federation-heavy; defer to v2.
- Studio promote modal / promote-live-to-baseline endpoint — descoped from v1 pending #507.
- Worker journey microspec — needs design iteration before build; file as separate design-spec issues.
- Determination sign-and-authorize ceremony — needs design before build.
- Notice compose/preview UX — needs design before build.
- Applicant portal — ADR-008, separate Dioxus project.
Stub plan
A stub docs/modules/ROOT/pages/plans/worker-portal-redesign.adoc will land in its own MR. The plan documents this issue's takeaways in plan-lifecycle form (per ADR-013) and tracks the discrete MRs as they ship. It's intentionally skeletal at the start — most Status rows will be "Not started" until the ADRs land. We can flesh it out as design questions get resolved and the architectural shape solidifies.
Cost estimate
Excluding marketplace + applicant portal + the explicit design gaps:
- ~18-20 MRs total across Stages 1-7
- ~6-10 weeks calendar if it's the engineering focus
- Less if some stages parallelize (e.g. Stage 1 design system extraction + Stage 2 ADRs can land in parallel)
Risk
The single biggest risk is Stage 3 (composability runtime). It's a real architectural commitment that touches: rendering pipeline, DB schema, override merge semantics, plugin lifecycle. If it lands without an ADR, half the codebase ends up partially migrated. The ADR-first sequencing (Stage 2 before Stage 3) is non-negotiable.
The handoff itself is opinionated and honest — it's not a "throw it over the wall" deliverable. The designer enumerated their own open questions and architectural decisions. That makes this issue more about engineering judgment than design discovery.