diff --git a/TODO.md b/TODO.md
index d6d330d..bc12158 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1334,6 +1334,25 @@ secao 134-139, 168)
são gerados, nunca editados à mão (consistente com "fechamento
imutável", secao 137)
+## PHASE 36 — Platform: Sistema > Permissões (agente.md secao 142-145,
+168)
+- [x] `GET /platform/roles` (novo) — roles do sistema com as permissions
+ de cada uma + catálogo completo de permissions. `roles`/
+ `permissions` não têm RLS (catálogo global, vem do seed). Só
+ leitura — RBAC é system-defined (`packages/auth/src/seed.ts`), sem
+ endpoint de criar/editar role customizada ainda.
+- [x] Frontend: `/platform/sistema/permissoes` — um card por role (nome,
+ key, escopo Plataforma/Tenant, badges com cada permission) + tabela
+ do catálogo completo de permissions com descrição.
+- [x] Testado ponta a ponta contra a API real: as 4 roles do sistema
+ (`platform_super_admin` 33 permissions, `tenant_admin` 30,
+ `supervisor` 17, `agent` 2) renderizadas corretamente com as
+ permissions certas. Smoke test de regressão nas 19 telas do tenant
+ + 9 telas platform, todas 200.
+- [ ] "Sistema > Configurações" continua "em breve" — nunca teve escopo
+ definido na especificação além do nome, mesma pendência já
+ documentada na PHASE 34
+
---
## Riscos conhecidos
diff --git a/apps/api/src/platform/platform-roles.controller.ts b/apps/api/src/platform/platform-roles.controller.ts
new file mode 100644
index 0000000..5a305e5
--- /dev/null
+++ b/apps/api/src/platform/platform-roles.controller.ts
@@ -0,0 +1,46 @@
+import { Controller, ForbiddenException, Get, UseGuards } from "@nestjs/common";
+import { getPrismaClient } from "@b2bcall/database";
+import { isPlatformUser, type AccessTokenClaims } from "@b2bcall/auth";
+import { JwtAuthGuard } from "../common/guards/jwt-auth.guard";
+import { PermissionGuard } from "../common/guards/permission.guard";
+import { RequirePermission } from "../common/decorators/require-permission.decorator";
+import { CurrentUser } from "../common/decorators/current-user.decorator";
+
+/**
+ * "Sistema > Permissões" (agente.md secao 142-145, 168) — só leitura por
+ * enquanto: RBAC é system-defined (roles/permissions vêm do seed,
+ * `packages/auth/src/seed.ts`), sem UI de criar role customizada ainda.
+ * `roles`/`permissions` não têm RLS (catálogo global do sistema).
+ */
+@UseGuards(JwtAuthGuard, PermissionGuard)
+@Controller("platform/roles")
+export class PlatformRolesController {
+ @RequirePermission("roles.manage")
+ @Get()
+ async list(@CurrentUser() user: AccessTokenClaims) {
+ if (!(await isPlatformUser(user.sub))) {
+ throw new ForbiddenException("So' um usuario com role de plataforma pode ver o catalogo de roles/permissoes");
+ }
+ const prisma = getPrismaClient();
+
+ const [roles, permissions] = await Promise.all([
+ prisma.role.findMany({
+ include: { rolePermissions: { include: { permission: true } } },
+ orderBy: { name: "asc" },
+ }),
+ prisma.permission.findMany({ orderBy: { key: "asc" } }),
+ ]);
+
+ return {
+ permissions,
+ roles: roles.map((r) => ({
+ id: r.id,
+ key: r.key,
+ name: r.name,
+ scope: r.scope,
+ isSystem: r.isSystem,
+ permissionKeys: r.rolePermissions.map((rp) => rp.permission.key),
+ })),
+ };
+ }
+}
diff --git a/apps/api/src/platform/platform.module.ts b/apps/api/src/platform/platform.module.ts
index 7a438c1..b6a29dd 100644
--- a/apps/api/src/platform/platform.module.ts
+++ b/apps/api/src/platform/platform.module.ts
@@ -3,8 +3,15 @@ import { PlatformOverviewController } from "./platform-overview.controller";
import { PlatformUsersController } from "./platform-users.controller";
import { PlatformAuditController } from "./platform-audit.controller";
import { PlatformHealthController } from "./platform-health.controller";
+import { PlatformRolesController } from "./platform-roles.controller";
@Module({
- controllers: [PlatformOverviewController, PlatformUsersController, PlatformAuditController, PlatformHealthController],
+ controllers: [
+ PlatformOverviewController,
+ PlatformUsersController,
+ PlatformAuditController,
+ PlatformHealthController,
+ PlatformRolesController,
+ ],
})
export class PlatformModule {}
diff --git a/apps/frontend/.impeccable/review/platform-permissoes-desktop.png b/apps/frontend/.impeccable/review/platform-permissoes-desktop.png
new file mode 100644
index 0000000..d182e30
Binary files /dev/null and b/apps/frontend/.impeccable/review/platform-permissoes-desktop.png differ
diff --git a/apps/frontend/src/app/platform/sistema/permissoes/page.tsx b/apps/frontend/src/app/platform/sistema/permissoes/page.tsx
new file mode 100644
index 0000000..c60abb3
--- /dev/null
+++ b/apps/frontend/src/app/platform/sistema/permissoes/page.tsx
@@ -0,0 +1,24 @@
+import { requireSession } from "@/lib/session";
+import { apiFetch } from "@/lib/api";
+import { PermissoesView } from "./permissoes-view";
+
+interface RoleWithPermissions {
+ id: string;
+ key: string;
+ name: string;
+ scope: "PLATFORM" | "TENANT";
+ isSystem: boolean;
+ permissionKeys: string[];
+}
+
+interface Permission {
+ id: string;
+ key: string;
+ description: string | null;
+}
+
+export default async function PermissoesPage() {
+ const session = await requireSession();
+ const data = await apiFetch<{ roles: RoleWithPermissions[]; permissions: Permission[] }>("/platform/roles", session.accessToken);
+ return
+ RBAC do sistema (agente.md secao 142-145) — roles e o catálogo de permissions são definidos no seed, sem + UI de criar role customizada ainda; esta tela é só leitura. +
+| Chave | +Descrição | +
|---|---|
| {p.key} | +{p.description ?? "—"} | +