feat: Worker portal Stage 3 — composition loader (TOML parse + role filter + override merge)
Description
Build the composition loader — the runtime component that resolves a (jurisdiction, role, user, surface) request into a rendered template tree by walking the 5-layer storage model top-down (user delta → role override → jurisdiction live → jurisdiction baseline TOML → system defaults).
This is the core of the composability runtime. Stage 3 cannot proceed past this issue.
Acceptance Criteria
-
crates/canopy-composition/(or whatever the Stage 2 composability-runtime ADR designates) holds the loader - Composition loader API:
load(jurisdiction, role, user, surface) -> ComposedSurfaceasyncComposedSurfacecarries panel/section list + per-item config (display_name, icon, span, programs)
- TOML parser handles jurisdiction baseline files at
rulesets/{jurisdiction}/composition/{surface}.toml - Role filter applies role-allowed permissions to filter out panels/sections the role can't see
- Override merge: per Stage 2 ADR's chosen merge semantics (RFC 7396 / RFC 6902 / full replace)
- Plugin manifest validator: validates
Plugin.tomlper Stage 2 composability ADR schema (slug unique, semver, allowed_spans subset, required roles exist, programs known) - Unit tests cover: TOML baseline only; baseline + jurisdiction live; baseline + role + user; conflicting layers (top wins); invalid Plugin.toml rejection
-
cargo nextest run -p canopy-compositionclean - CHANGELOG entry under
=== Added
Blocked by
- Stage 2 composability-runtime ADR merged
- Stage 3 DB migrations issue merged (for override-table reads)
Context & References
- Tracking issue: #460
- Epic: &51
- Plan: worker-portal-redesign.adoc, Stage 3
- Related: Stage 2 ADRs (composability runtime, storage layering)
Labels
type::feature, priority::medium, program::infrastructure, service::web, workflow::needs-spec