feat(app): M5.2 — customer-area route shells + role-gated nav #6
Reference in New Issue
Block a user
Delete Branch "feat/m5.2-shells"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
M5.2 in full: 10 customer-area route shells + a role-gated nav + a backstage stub.
/[slug]/): products, projects, catalog, settings, settings/users, settings/api-keys, settings/integrations, billing, audit, support. Each renders an empty-state pointing at the milestone that ships its real content. 403 if the user'sorg_rolesdon't grant the surface (per the matrix insrc/lib/session.ts).session.org_rolesand filters links so users only see what they're allowed to access (PLATFORM_ARCHITECTURE.md §5a 'hide what the user can't access').session.productsand renders one tile per entitlement. Empty-state links into the catalog flow./__backstage__— middleware already rewritesbackstage.<apex>/*to this prefix. Real RBAC against BREAKPILOT_ADMIN / SUPPORT_ENGINEER / SALES_REP lands in M13.2.[slug]/layout.tsx: a session withtenant_slug=Atrying to view/B/...gets redirected to/A/dashboard. Defence-in-depth; the real guard is at the API layer (M4.3).Why
After M4.1–M4.3 there's a real backend that knows everything about a tenant + their entitlements + their KC user. M5.2 makes the portal actually navigable. Every M10.x / M11.x / M12.x / M14.x milestone will replace one of these shells with real content — by landing the URLs + RBAC gates now, none of those PRs needs to touch routing.
Linked milestone: M5.2
How
src/lib/session.tsis the single source of truth for the role → surface matrix.canSee(session, surface)is the only check pages do — never enumerate roles inline. 13 vitest cases assert every role × surface combination.<ShellEmpty title description milestone />component so every surface looks identical and the cross-link to the right milestone is consistent./__backstage__/so it can't be confused with a tenant slug (the host parser already filters it via{ kind: 'backstage' }, but the path prefix is the second wall).pnpm lint+typecheck+test --coverage(100% onsrc/lib) +build. 24 tests now, was 13.Test plan
pnpm lintcleanpnpm typecheckcleanpnpm test— 24 tests, 100%src/libcoverage (host + session + tenant-registry)pnpm build— all routes compile in standalone modeRisk
Blast radius: portal-only. Defaults are conservative — shells render "this is M-whatever" placeholders, no destructive surfaces.
What could break:
src/lib/session.ts+ add a test case.canSee()is the only RBAC primitive in the portal. It is not an auth check — it just hides links. The API layer (tenant-registry M4.3+) does the real enforcement. A motivated user pasting URLs gets to the page; without a valid JWT scope the API returns 401/403 and the page renders an error state.Rollback plan: revert. Existing portal routes (
/apex,/[slug],/[slug]/dashboard) keep working.Checklist
Pull request closed