Child of PAR-182. UI side of RBAC (tesks-ui).
deleteIssueAction uses requireWorkspaceWrite — should be manage-level (lib/actions/issues.ts:106); button in issues/[ref]/page.tsx:64 renders for any writer.setMemberRoleAction/removeMemberAction (lib/actions/members.ts). Add guards mirroring the backend.owner|member|viewer union (lib/roles.ts:3) with roles fetched from the workspace; add a roles management screen in settings to create/edit roles + permissions (admin-gated). Defaults admin/contributor/viewer.staff super-role: lib/workspace.ts:57-59 injects Keycloak staff as a member of every tenant — fine for internal ops but document/flag it so it isn't mistaken for tenant isolation.lib/roles.ts) become permission-keyed, sourced from the backend rather than hard-coded unions.Acceptance: UI affordances match server-side permissions; non-admins can't see/perform manage actions; owners are protected; an admin can define a custom role and assign it.
Phase 1 done on develop (tesks-ui commit 87698db) — RBAC is now permission-driven end-to-end:
Workspace carries the viewer's resolved permission keys (from the V19 role/role_permission mirror tables; staff get the admin set). lib/roles exposes canWrite/canDeleteIssues/canManageMembers/canManageSettings/canManageRoles/canManage over them; the old owner|member|viewer union is gone.lib/api forwards X-Actor-Staff → backend staff break-glass works.settings.manage; issue delete (now a soft Archive) → issue.delete; members → member.manage. Settings area opens to anyone with member- or settings-management.Remaining — Phase 2: custom-role CRUD UI. Members can now be assigned any of the seeded roles, but there's no screen yet to create/edit/delete custom roles. That needs new backend endpoints (WorkspaceAdminService create/update/delete role + WorkspaceApiController routes, gated on role.manage, protecting the owner/system roles + in-use roles) plus a Roles section in settings with per-permission checkboxes. Owner-only powers (issue.destroy, workspace.manage) stay non-assignable to custom roles.
Phase 2 done on develop — custom-role CRUD shipped (tesks commit 061dba7, tesks-ui commit 096c5c7):
WorkspaceAdminService.createRole/updateRole/deleteRole + /api/w/{slug}/roles endpoints, gated on role.manage. Owner role immutable, built-in roles undeletable, in-use roles undeletable; owner-only powers (issue.destroy/workspace.manage) not assignable to custom roles. 4 tests (backend suite 112 green).role.manage) — create custom roles, edit a role's permissions via checkboxes, delete unused ones; owner shows as fixed.Verified: tesks-ui tsc + vitest + next build all green.
Core RBAC for PAR-187 is complete and shipped → moving to Pending Release. The two remaining minor polish items (API-key mint role selector, friendlier 403 surfacing) are split out as PAR-195 (Low).
Verified LIVE — all four criteria: (1) deleteIssueAction gated by requireWorkspaceDelete/canDeleteIssues (issue.delete, matching the backend ISSUE_DELETE archive gate) and the delete button only renders for canDelete; (2) owner/last-owner protection enforced server-side (member actions defer to backend rules); (3) lib/roles.ts no longer a hard-coded union (Role = string), roles fetched per workspace via lib/sql/roles.listRoles; (4) roles management screen in settings/page.tsx (create/edit/delete + permission checkboxes, canManageRoles-gated). Acceptance met.
Backend dependencies for the UI work are now in place (commits
25680a9,c7d4aa0). When building this:X-Actor-Stafffrom the UI's server-to-server calls for Keycloakstaffusers — the backend grants them admin-equivalent rights in any workspace only when this header is present (otherwise staff who aren't members get 403 on managed writes).owner|member|viewerunion inlib/roles.ts. Add the roles management screen (admin-gated) backed by therole/role_permissiontables.ApiKeyService.create(name, by, roleSlug)already supports it.{ "error": ... }; surface them.