Replace the FE-computed authorization anti-pattern in BriefStore.editable (derived from the unverified X-Role header) with server-computed decision flags, mirroring the existing HerregistratieDecisionsDto pattern: - Backend: Authz.cs is the single authorization helper — the SAME check (Authz.CanActOn) both gates BriefStore.Review's mutations and computes the BriefDecisionsDto flags shipped on every brief response, so emit and enforce can never drift. New GET /me returns coarse, role-derived capabilities (PRD-0002 SS6). - Every brief endpoint (including send, previously ungated on HttpContext) now returns a fresh BriefViewDto so decisions never go stale after a mutation. - FE: brief.store.ts reads canEdit/canApprove/canReject/canSend off the loaded decisions instead of computing them from currentRole(); the brief.machine carries decisions through every status transition. - New shared/domain/capability.ts + shared/application/access.store.ts + shared/infrastructure/me.adapter.ts: the general capability-spine infrastructure (AccessStore.can(), capabilityGuard) for future routes. Deviates from the original WP-18 draft by NOT renaming auth/domain's Session to a Principal union — ADR-0002 explicitly defers that refactor until a second actor exists, and the brief workflow's drafter/approver identity turned out to be a separate axis from the SSP login session entirely. See docs/backlog/WP-18-abac-capability-spine.md for the full as-built record. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.9 KiB
Backlog — showcase hardening
Ordered work packages that take this POC from "good" to reference showcase: CIBG design-system fidelity, DDD/FP consistency, Storybook as curriculum, and WCAG compliance with automated gates. Source: the architecture/CIBG/a11y audit of 2026-07-02 (plan: "Showcase hardening").
This backlog supersedes docs/SHOWCASE-ROADMAP.md.
Session protocol
- Switch to Opus first (
/model opus) before tackling any WP. - One WP per session. Read
CLAUDE.md, this README, the WP file, and the WP's "Read first" list — then execute. Do not start the next WP in the same session. - The Decisions block in each WP is pre-made — don't relitigate it.
- A WP ends GREEN (below) with its acceptance criteria checked off and its Status
updated to
done(+ commit hash). - No WP leaves a lint rule/check disabled without an inline justification comment and a cross-reference to the WP that will remove it.
GREEN (global definition of done)
npm run lint && npm run check:tokens && npm test && npm run build && npm run build-storybook
From WP-01 onward, additionally:
npm run test-storybook:ci
Backend stays untouched throughout (frontend-only backlog); cd backend && dotnet test
only needs re-running if a WP unexpectedly touches backend/.
Order
Gates land before the work they cover; each lint rule lands in the same WP as the fixes for its existing violations, so every WP ends green.
| WP | Title | Phase | Status |
|---|---|---|---|
| WP-01 | Axe-on-every-story CI gate | 0 · gates | done |
| WP-02 | Harden check:tokens + fix what it catches |
0 · gates | done |
| WP-03 | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done |
| WP-04 | Boundaries II: ui ↛ infrastructure + showcase sanction |
0 · gates | done |
| WP-05 | Parse-don't-validate closure + MDX | 1 · FP/DDD | todo |
| WP-06 | Generic async template contexts — kill $any() |
1 · FP/DDD | todo |
| WP-07 | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | todo |
| WP-08 | One store idiom + machine naming + TEA MDX | 1 · FP/DDD | todo |
| WP-09 | Pure-logic closure: dates + missing command specs | 1 · FP/DDD | todo |
| WP-10 | CIBG button fidelity | 2 · CIBG | todo |
| WP-11 | CIBG markup fidelity: application-link + absent-class triage | 2 · CIBG | done |
| WP-12 | CIBG Datablock for application data | 2 · CIBG | done |
| WP-13 | CIBG-gap register + hygiene + MDX | 2 · CIBG | todo |
| WP-14 | Storybook taxonomy reorg + Layers MDX | 3 · Storybook | todo |
| WP-15 | Missing stories: shell + brief components | 3 · Storybook | todo |
| WP-16 | Component a11y: description wiring + alert role | 4 · a11y | todo |
| WP-17 | App-level a11y: route focus, template lint, WCAG checklist | 4 · a11y | todo |
| WP-18 | ABAC capability spine (Principal + capabilities, phase P1) | 5 · productie-volwassenheid | done |
| WP-19 | Playwright e2e smoke | 5 · productie-volwassenheid | todo |
| WP-20 | Second locale proof | 5 · productie-volwassenheid | todo |
| WP-21 | Resilience seams (correlation-id, idempotency, retry) | 5 · productie-volwassenheid | todo |
| WP-22 | Durable persistence (optional tier) | 5 · productie-volwassenheid | todo |
Sequencing dependencies (stated in the WPs too): 01 before 10–15 (axe covers story churn);
03/04 before 05–09 (boundaries stop new violations during refactors); 06 before 07 (typed
<app-async> before brief adopts it); 13 defines the gap-marker format that 11/12 reference
— if 11/12 run first, they define it and 13 adopts it. 18–22 (phase 5, "productie-volwassenheid")
are independent of each other and of phases 1–4 — pick any order; 18 is the recommended
first pick (it's the headline gap: no authorization spine exists yet, and it closes the
FE-computed-authz anti-pattern in brief.store.ts). 22 is explicitly lower priority — the
current in-memory persistence is a documented, defensible POC choice, not a bug.
WP template
# WP-NN — Title
Status: todo | in-progress | done (<commit>)
Phase: N — name
## Why
## Read first
## Decisions (pre-made, don't relitigate)
## Files
## Steps
## Acceptance criteria
## Verification
## Out of scope
## Risks