@luanpdd/kit-mcp 1.20.0 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +648 -648
  3. package/gates/dept-cycle-prevention.md +179 -0
  4. package/gates/multi-tenant-rls-coverage.md +102 -0
  5. package/gates/service-role-not-in-user-facing.md +113 -0
  6. package/kit/COMANDOS.md +138 -138
  7. package/kit/README.md +52 -52
  8. package/kit/agents/advisor-researcher.md +106 -106
  9. package/kit/agents/assumptions-analyzer.md +107 -107
  10. package/kit/agents/audit-log-implementer.md +175 -0
  11. package/kit/agents/b2b-saas-architect.md +156 -0
  12. package/kit/agents/codebase-mapper.md +768 -768
  13. package/kit/agents/crm-pipeline-implementer.md +150 -0
  14. package/kit/agents/debugger.md +772 -772
  15. package/kit/agents/evolution-go-integrator.md +179 -0
  16. package/kit/agents/example-reviewer.md +21 -21
  17. package/kit/agents/executor.md +523 -523
  18. package/kit/agents/integration-checker.md +200 -200
  19. package/kit/agents/invite-flow-implementer.md +137 -0
  20. package/kit/agents/lgpd-compliance-auditor.md +206 -0
  21. package/kit/agents/multi-tenant-isolation-auditor.md +243 -0
  22. package/kit/agents/multi-tenant-rls-writer.md +262 -0
  23. package/kit/agents/nyquist-auditor.md +178 -178
  24. package/kit/agents/org-onboarding-implementer.md +202 -0
  25. package/kit/agents/phase-researcher.md +696 -696
  26. package/kit/agents/plan-checker.md +272 -272
  27. package/kit/agents/planner.md +891 -891
  28. package/kit/agents/project-researcher.md +652 -652
  29. package/kit/agents/research-synthesizer.md +245 -245
  30. package/kit/agents/roadmapper.md +677 -677
  31. package/kit/agents/super-admin-implementer.md +182 -0
  32. package/kit/agents/ui-auditor.md +437 -437
  33. package/kit/agents/ui-checker.md +302 -302
  34. package/kit/agents/ui-researcher.md +355 -355
  35. package/kit/agents/user-profiler.md +175 -175
  36. package/kit/agents/verifier.md +728 -728
  37. package/kit/commands/adicionar-backlog.md +75 -75
  38. package/kit/commands/adicionar-fase.md +42 -42
  39. package/kit/commands/adicionar-tarefa.md +45 -45
  40. package/kit/commands/adicionar-testes.md +41 -41
  41. package/kit/commands/ajuda.md +21 -21
  42. package/kit/commands/atualizar.md +37 -37
  43. package/kit/commands/auditar-marco.md +179 -179
  44. package/kit/commands/auditar-uat.md +23 -23
  45. package/kit/commands/autonomo.md +40 -40
  46. package/kit/commands/branch-pr.md +24 -24
  47. package/kit/commands/concluir-marco.md +247 -247
  48. package/kit/commands/configuracoes.md +36 -36
  49. package/kit/commands/definir-perfil.md +10 -10
  50. package/kit/commands/depurar.md +190 -190
  51. package/kit/commands/discutir-fase.md +131 -131
  52. package/kit/commands/entrar-discord.md +17 -17
  53. package/kit/commands/estatisticas.md +18 -18
  54. package/kit/commands/example-greeting.md +33 -33
  55. package/kit/commands/executar-fase.md +58 -58
  56. package/kit/commands/expresso.md +56 -56
  57. package/kit/commands/fase-ui.md +34 -34
  58. package/kit/commands/fazer.md +57 -57
  59. package/kit/commands/fio.md +125 -125
  60. package/kit/commands/fluxos-trabalho.md +64 -64
  61. package/kit/commands/forense.md +176 -176
  62. package/kit/commands/gerenciador.md +38 -38
  63. package/kit/commands/inserir-fase.md +31 -31
  64. package/kit/commands/limpeza.md +17 -17
  65. package/kit/commands/listar-hipoteses-fase.md +45 -45
  66. package/kit/commands/listar-workspaces.md +18 -18
  67. package/kit/commands/mapear-codebase.md +70 -70
  68. package/kit/commands/multi-tenant.md +163 -0
  69. package/kit/commands/nota.md +33 -33
  70. package/kit/commands/novo-marco.md +43 -43
  71. package/kit/commands/novo-projeto.md +41 -41
  72. package/kit/commands/novo-workspace.md +43 -43
  73. package/kit/commands/pausar-trabalho.md +37 -37
  74. package/kit/commands/perfil-usuario.md +45 -45
  75. package/kit/commands/pesquisar-fase.md +195 -195
  76. package/kit/commands/planejar-fase.md +67 -67
  77. package/kit/commands/planejar-lacunas.md +33 -33
  78. package/kit/commands/plantar-ideia.md +25 -25
  79. package/kit/commands/progresso.md +24 -24
  80. package/kit/commands/proximo.md +30 -30
  81. package/kit/commands/publicar.md +490 -490
  82. package/kit/commands/rapido.md +35 -35
  83. package/kit/commands/reaplicar-patches.md +124 -124
  84. package/kit/commands/relatorio-sessao.md +19 -19
  85. package/kit/commands/remover-fase.md +31 -31
  86. package/kit/commands/remover-workspace.md +26 -26
  87. package/kit/commands/resumo-marco.md +50 -50
  88. package/kit/commands/retomar-trabalho.md +40 -40
  89. package/kit/commands/revisar-backlog.md +60 -60
  90. package/kit/commands/revisar-ui.md +32 -32
  91. package/kit/commands/revisar.md +37 -37
  92. package/kit/commands/saude.md +21 -21
  93. package/kit/commands/setup-notion.md +93 -93
  94. package/kit/commands/sync-main.md +68 -68
  95. package/kit/commands/validar-fase.md +35 -35
  96. package/kit/commands/verificar-tarefas.md +44 -44
  97. package/kit/commands/verificar-trabalho.md +64 -64
  98. package/kit/file-manifest.json +30 -3
  99. package/kit/framework/bin/lib/commands.cjs +959 -959
  100. package/kit/framework/bin/lib/config.cjs +442 -442
  101. package/kit/framework/bin/lib/core.cjs +1230 -1230
  102. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  103. package/kit/framework/bin/lib/init.cjs +1442 -1442
  104. package/kit/framework/bin/lib/milestone.cjs +252 -252
  105. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  106. package/kit/framework/bin/lib/phase.cjs +888 -888
  107. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  108. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  109. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  110. package/kit/framework/bin/lib/security.cjs +382 -382
  111. package/kit/framework/bin/lib/state.cjs +1031 -1031
  112. package/kit/framework/bin/lib/template.cjs +222 -222
  113. package/kit/framework/bin/lib/uat.cjs +282 -282
  114. package/kit/framework/bin/lib/verify.cjs +888 -888
  115. package/kit/framework/bin/lib/workstream.cjs +491 -491
  116. package/kit/framework/bin/tools.cjs +918 -918
  117. package/kit/framework/commands/workstreams.md +63 -63
  118. package/kit/framework/references/checkpoints.md +778 -778
  119. package/kit/framework/references/continuation-format.md +249 -249
  120. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  121. package/kit/framework/references/git-integration.md +295 -295
  122. package/kit/framework/references/git-planning-commit.md +38 -38
  123. package/kit/framework/references/model-profile-resolution.md +36 -36
  124. package/kit/framework/references/model-profiles.md +139 -139
  125. package/kit/framework/references/phase-argument-parsing.md +61 -61
  126. package/kit/framework/references/planning-config.md +202 -202
  127. package/kit/framework/references/questioning.md +162 -162
  128. package/kit/framework/references/tdd.md +263 -263
  129. package/kit/framework/references/ui-brand.md +160 -160
  130. package/kit/framework/references/user-profiling.md +657 -657
  131. package/kit/framework/references/verification-patterns.md +612 -612
  132. package/kit/framework/references/workstream-flag.md +58 -58
  133. package/kit/framework/templates/DEBUG.md +164 -164
  134. package/kit/framework/templates/UAT.md +265 -265
  135. package/kit/framework/templates/UI-SPEC.md +100 -100
  136. package/kit/framework/templates/VALIDATION.md +76 -76
  137. package/kit/framework/templates/claude-md.md +122 -122
  138. package/kit/framework/templates/codebase/architecture.md +185 -185
  139. package/kit/framework/templates/codebase/concerns.md +205 -205
  140. package/kit/framework/templates/codebase/conventions.md +204 -204
  141. package/kit/framework/templates/codebase/integrations.md +192 -192
  142. package/kit/framework/templates/codebase/stack.md +158 -158
  143. package/kit/framework/templates/codebase/structure.md +199 -199
  144. package/kit/framework/templates/codebase/testing.md +301 -301
  145. package/kit/framework/templates/config.json +44 -44
  146. package/kit/framework/templates/context.md +352 -352
  147. package/kit/framework/templates/continue-here.md +78 -78
  148. package/kit/framework/templates/copilot-instructions.md +7 -7
  149. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  150. package/kit/framework/templates/dev-preferences.md +20 -20
  151. package/kit/framework/templates/discovery.md +146 -146
  152. package/kit/framework/templates/discussion-log.md +63 -63
  153. package/kit/framework/templates/milestone-archive.md +123 -123
  154. package/kit/framework/templates/milestone.md +115 -115
  155. package/kit/framework/templates/phase-prompt.md +610 -610
  156. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  157. package/kit/framework/templates/project.md +186 -186
  158. package/kit/framework/templates/requirements.md +231 -231
  159. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  160. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  161. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  162. package/kit/framework/templates/research-project/STACK.md +120 -120
  163. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  164. package/kit/framework/templates/research.md +419 -419
  165. package/kit/framework/templates/retrospective.md +54 -54
  166. package/kit/framework/templates/roadmap.md +202 -202
  167. package/kit/framework/templates/state.md +176 -176
  168. package/kit/framework/templates/summary-complex.md +59 -59
  169. package/kit/framework/templates/summary-minimal.md +41 -41
  170. package/kit/framework/templates/summary-standard.md +48 -48
  171. package/kit/framework/templates/summary.md +209 -209
  172. package/kit/framework/templates/user-profile.md +146 -146
  173. package/kit/framework/templates/user-setup.md +256 -256
  174. package/kit/framework/templates/verification-report.md +258 -258
  175. package/kit/framework/workflows/add-phase.md +112 -112
  176. package/kit/framework/workflows/add-tests.md +351 -351
  177. package/kit/framework/workflows/add-todo.md +158 -158
  178. package/kit/framework/workflows/audit-milestone.md +340 -340
  179. package/kit/framework/workflows/audit-uat.md +109 -109
  180. package/kit/framework/workflows/autonomous.md +891 -891
  181. package/kit/framework/workflows/check-todos.md +177 -177
  182. package/kit/framework/workflows/cleanup.md +152 -152
  183. package/kit/framework/workflows/complete-milestone.md +696 -696
  184. package/kit/framework/workflows/diagnose-issues.md +231 -231
  185. package/kit/framework/workflows/discovery-phase.md +289 -289
  186. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  187. package/kit/framework/workflows/discuss-phase.md +784 -784
  188. package/kit/framework/workflows/do.md +104 -104
  189. package/kit/framework/workflows/execute-phase.md +838 -838
  190. package/kit/framework/workflows/execute-plan.md +510 -510
  191. package/kit/framework/workflows/fast.md +102 -102
  192. package/kit/framework/workflows/forensics.md +265 -265
  193. package/kit/framework/workflows/health.md +181 -181
  194. package/kit/framework/workflows/help.md +619 -619
  195. package/kit/framework/workflows/insert-phase.md +130 -130
  196. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  197. package/kit/framework/workflows/list-workspaces.md +56 -56
  198. package/kit/framework/workflows/manager.md +362 -362
  199. package/kit/framework/workflows/map-codebase.md +377 -377
  200. package/kit/framework/workflows/milestone-summary.md +223 -223
  201. package/kit/framework/workflows/new-milestone.md +486 -486
  202. package/kit/framework/workflows/new-project.md +1159 -1159
  203. package/kit/framework/workflows/new-workspace.md +237 -237
  204. package/kit/framework/workflows/next.md +97 -97
  205. package/kit/framework/workflows/node-repair.md +92 -92
  206. package/kit/framework/workflows/note.md +156 -156
  207. package/kit/framework/workflows/pause-work.md +176 -176
  208. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  209. package/kit/framework/workflows/plan-phase.md +765 -765
  210. package/kit/framework/workflows/plant-seed.md +169 -169
  211. package/kit/framework/workflows/pr-branch.md +129 -129
  212. package/kit/framework/workflows/profile-user.md +450 -450
  213. package/kit/framework/workflows/progress.md +507 -507
  214. package/kit/framework/workflows/quick.md +757 -757
  215. package/kit/framework/workflows/remove-phase.md +155 -155
  216. package/kit/framework/workflows/remove-workspace.md +90 -90
  217. package/kit/framework/workflows/research-phase.md +82 -82
  218. package/kit/framework/workflows/resume-project.md +326 -326
  219. package/kit/framework/workflows/review.md +228 -228
  220. package/kit/framework/workflows/session-report.md +146 -146
  221. package/kit/framework/workflows/settings.md +283 -283
  222. package/kit/framework/workflows/ship.md +228 -228
  223. package/kit/framework/workflows/stats.md +60 -60
  224. package/kit/framework/workflows/transition.md +671 -671
  225. package/kit/framework/workflows/ui-phase.md +302 -302
  226. package/kit/framework/workflows/ui-review.md +165 -165
  227. package/kit/framework/workflows/update.md +323 -323
  228. package/kit/framework/workflows/validate-phase.md +174 -174
  229. package/kit/framework/workflows/verify-phase.md +252 -252
  230. package/kit/framework/workflows/verify-work.md +637 -637
  231. package/kit/hooks/check-update.js +118 -118
  232. package/kit/hooks/context-monitor.js +163 -163
  233. package/kit/hooks/prompt-guard.js +103 -103
  234. package/kit/hooks/statusline.js +125 -125
  235. package/kit/hooks/workflow-guard.js +101 -101
  236. package/kit/settings.json +45 -45
  237. package/kit/skills/_shared-multi-tenant/glossary.md +186 -0
  238. package/kit/skills/audit-log-multi-tenant/SKILL.md +334 -0
  239. package/kit/skills/b2b-saas-architecture/SKILL.md +300 -0
  240. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +326 -0
  241. package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -0
  242. package/kit/skills/example-skill/SKILL.md +42 -42
  243. package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -0
  244. package/kit/skills/member-invite-flow/SKILL.md +305 -0
  245. package/kit/skills/member-management-react-shadcn/SKILL.md +328 -0
  246. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +312 -0
  247. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +338 -0
  248. package/kit/skills/org-onboarding-flow/SKILL.md +257 -0
  249. package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -0
  250. package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -0
  251. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +301 -0
  252. package/kit/skills/super-admin-platform-pattern/SKILL.md +322 -0
  253. package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -0
  254. package/package.json +63 -63
  255. package/src/core/kit.js +216 -216
  256. package/src/core/reflect.js +247 -247
  257. package/src/core/reverse-sync.js +372 -372
  258. package/src/core/sync.js +418 -418
  259. package/src/core/watch.js +121 -121
@@ -0,0 +1,334 @@
1
+ ---
2
+ name: audit-log-multi-tenant
3
+ description: Use ao implementar audit log em B2B SaaS multi-tenant Supabase — tabela append-only (REVOKE DELETE/UPDATE), 7 event types canônicos, retention pg_cron 3 tiers (30d/90d/365d), legal_hold flag para LGPD erasure, PII sanitization (hash actor_email), tenant_id obrigatório indexed.
4
+ ---
5
+
6
+ # Audit Log Multi-Tenant — Compliance + Forensics
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill ao implementar audit log em B2B SaaS multi-tenant. Trigger phrases:
11
+
12
+ - "audit log multi-tenant", "audit trail compliance"
13
+ - "append-only table Postgres", "REVOKE DELETE UPDATE"
14
+ - "retention pg_cron tiers"
15
+ - "legal hold LGPD erasure"
16
+ - "PII sanitization audit"
17
+ - "event taxonomy canônica"
18
+
19
+ Esta skill é consumida pelo agent `audit-log-implementer` (Phase 109) que materializa migration + retention scheduler.
20
+
21
+ ## Regras absolutas
22
+
23
+ **REGRA #1 (append-only):** Tabela `audit_logs` é **append-only** via `REVOKE DELETE, UPDATE ON public.audit_logs FROM authenticated`. Apenas service_role pode mutar (via partition swap, raramente). Comprometimento de admin não consegue apagar evidências.
24
+
25
+ **REGRA #2 (tenant_id obrigatório indexed first):** Toda row em `audit_logs` tem `tenant_id` (= `org_id`) **NOT NULL** + index composite `(tenant_id, created_at desc)` como **primeira** coluna. Sem isso, queries "todos eventos da org X" viram table scan.
26
+
27
+ **REGRA #3 (legal_hold flag):** Coluna `legal_hold boolean default false`. Quando user da org X faz DSR LGPD de erasure, marcar `legal_hold = true` em todas suas rows pendentes — bloqueia delete pelo retention scheduler até DSR processada.
28
+
29
+ **REGRA #4 (PII sanitization):** `actor_email` e `target_email` armazenados como **SHA-256 hash** (não raw). Para investigação, forensic mode rehasha email candidato e busca match. PII em log = LGPD violation.
30
+
31
+ **REGRA #5 (retention 3 tiers):** Default retention via pg_cron:
32
+ - **Free tier**: 30 dias
33
+ - **Pro tier**: 90 dias
34
+ - **Enterprise tier**: 365 dias
35
+ - **Sempre respeitando legal_hold = true** (skip rows com legal hold)
36
+
37
+ **REGRA #6 (event taxonomy mínima — 7 events):** `login`, `member_invited`, `role_changed`, `data_exported`, `member_removed`, `settings_changed`, `super_admin_action`. Custom events permitidos via campo `event_type text` mas estes 7 são obrigatórios em qualquer app B2B.
38
+
39
+ ## Patterns canônicos
40
+
41
+ ### Tabela `audit_logs` — DDL completo
42
+
43
+ ```sql
44
+ -- Tabela append-only, particionada por tenant_id (LIST partitioning) se >50k rows/tenant
45
+ create table public.audit_logs (
46
+ id uuid not null default gen_random_uuid(),
47
+ tenant_id uuid not null references public.organizations(id) on delete cascade,
48
+ event_type text not null check (event_type ~ '^[a-z_]+$'),
49
+ actor_id uuid references auth.users(id) on delete set null, -- NULL após erasure
50
+ actor_email_hash text, -- SHA-256, REGRA #4
51
+ target_id uuid, -- ID do recurso afetado (lead, member, etc.)
52
+ target_type text, -- 'lead', 'member', 'org', etc.
53
+ target_email_hash text, -- SHA-256 do email se aplicável
54
+ payload jsonb, -- detalhes do evento (campos changed, etc.)
55
+ ip_address inet, -- IP de origem (opcional)
56
+ user_agent text, -- UA (opcional)
57
+ legal_hold boolean not null default false, -- REGRA #3
58
+ created_at timestamptz not null default now(),
59
+ primary key (id, tenant_id), -- composite para particionamento
60
+ -- REGRA #2: index composite tenant_id first
61
+ constraint event_type_canonical check (
62
+ event_type in (
63
+ 'login', 'member_invited', 'role_changed', 'data_exported',
64
+ 'member_removed', 'settings_changed', 'super_admin_action'
65
+ ) or event_type ~ '^custom_[a-z_]+$' -- custom events com prefix
66
+ )
67
+ );
68
+
69
+ -- Index composite tenant_id first (REGRA #2)
70
+ create index audit_logs_tenant_created_idx
71
+ on public.audit_logs (tenant_id, created_at desc);
72
+
73
+ -- Index para busca por actor (compliance investigation)
74
+ create index audit_logs_actor_id_idx
75
+ on public.audit_logs (actor_id, created_at desc) where actor_id is not null;
76
+
77
+ -- Index para legal hold (rotina retention precisa filtrar)
78
+ create index audit_logs_legal_hold_idx
79
+ on public.audit_logs (legal_hold, created_at) where legal_hold = false;
80
+
81
+ -- REGRA #1: append-only — REVOKE DELETE e UPDATE
82
+ alter table public.audit_logs enable row level security;
83
+
84
+ revoke delete on public.audit_logs from authenticated, anon;
85
+ revoke update on public.audit_logs from authenticated, anon;
86
+ -- service_role bypassa RLS — partition swap futuro é o único delete legítimo
87
+
88
+ -- POLICY SELECT: members da org com permission view:audit_logs
89
+ create policy "audit_logs_select_with_permission"
90
+ on public.audit_logs
91
+ for select
92
+ to authenticated
93
+ using (
94
+ private.has_permission('view', 'audit_logs', tenant_id)
95
+ );
96
+
97
+ -- POLICY INSERT: qualquer authenticated pode inserir (helper function escreve via SECURITY DEFINER)
98
+ create policy "audit_logs_insert_authenticated"
99
+ on public.audit_logs
100
+ for insert
101
+ to authenticated
102
+ with check (
103
+ tenant_id is not null
104
+ and event_type is not null
105
+ );
106
+
107
+ -- POLICY super_admin bypass (PERMISSIVE)
108
+ create policy "audit_logs_super_admin_bypass"
109
+ on public.audit_logs
110
+ as permissive
111
+ for select
112
+ to authenticated
113
+ using (private.is_super_admin());
114
+ ```
115
+
116
+ ### Helper function — emit audit event
117
+
118
+ ```sql
119
+ -- SECURITY DEFINER porque precisa hash email mesmo se user não tiver INSERT direto
120
+ create or replace function private.audit_log(
121
+ p_event_type text,
122
+ p_tenant_id uuid,
123
+ p_target_id uuid default null,
124
+ p_target_type text default null,
125
+ p_target_email text default null,
126
+ p_payload jsonb default null
127
+ )
128
+ returns void
129
+ language plpgsql
130
+ security definer
131
+ set search_path = ''
132
+ as $$
133
+ declare
134
+ v_actor_id uuid;
135
+ v_actor_email text;
136
+ begin
137
+ v_actor_id := (select auth.uid());
138
+ select email into v_actor_email from auth.users where id = v_actor_id;
139
+
140
+ insert into public.audit_logs (
141
+ tenant_id,
142
+ event_type,
143
+ actor_id,
144
+ actor_email_hash,
145
+ target_id,
146
+ target_type,
147
+ target_email_hash,
148
+ payload
149
+ )
150
+ values (
151
+ p_tenant_id,
152
+ p_event_type,
153
+ v_actor_id,
154
+ case when v_actor_email is not null then encode(digest(v_actor_email, 'sha256'), 'hex') end,
155
+ p_target_id,
156
+ p_target_type,
157
+ case when p_target_email is not null then encode(digest(p_target_email, 'sha256'), 'hex') end,
158
+ p_payload
159
+ );
160
+ end;
161
+ $$;
162
+
163
+ -- Permitir authenticated chamar
164
+ grant execute on function private.audit_log(text, uuid, uuid, text, text, jsonb) to authenticated;
165
+ ```
166
+
167
+ ### Retention via pg_cron — 3 tiers
168
+
169
+ ```sql
170
+ -- Schedule diário 03:00 UTC — apaga rows velhas respeitando legal_hold
171
+ select cron.schedule(
172
+ 'audit-log-retention',
173
+ '0 3 * * *',
174
+ $$
175
+ -- Free tier orgs: 30 dias
176
+ delete from public.audit_logs al
177
+ using public.organizations o
178
+ where al.tenant_id = o.id
179
+ and o.plan = 'free'
180
+ and al.created_at < now() - interval '30 days'
181
+ and al.legal_hold = false;
182
+
183
+ -- Pro tier orgs: 90 dias
184
+ delete from public.audit_logs al
185
+ using public.organizations o
186
+ where al.tenant_id = o.id
187
+ and o.plan = 'pro'
188
+ and al.created_at < now() - interval '90 days'
189
+ and al.legal_hold = false;
190
+
191
+ -- Enterprise tier orgs: 365 dias
192
+ delete from public.audit_logs al
193
+ using public.organizations o
194
+ where al.tenant_id = o.id
195
+ and o.plan = 'enterprise'
196
+ and al.created_at < now() - interval '365 days'
197
+ and al.legal_hold = false;
198
+ $$
199
+ );
200
+ ```
201
+
202
+ **Nota:** o DELETE acima é executado pelo pg_cron com role `postgres` (bypassa RLS). NÃO é `authenticated` — REGRA #1 não é violada.
203
+
204
+ ### Emit eventos canônicos — exemplos
205
+
206
+ ```sql
207
+ -- Login (após auth callback)
208
+ select private.audit_log(
209
+ p_event_type := 'login',
210
+ p_tenant_id := <org_id>,
211
+ p_payload := jsonb_build_object('ip', '<ip>', 'user_agent', '<ua>')
212
+ );
213
+
214
+ -- Member invited
215
+ select private.audit_log(
216
+ p_event_type := 'member_invited',
217
+ p_tenant_id := <org_id>,
218
+ p_target_id := <invited_user_id>,
219
+ p_target_email := <invited_email>,
220
+ p_payload := jsonb_build_object('role', 'admin', 'invited_by', '<actor_email_hash>')
221
+ );
222
+
223
+ -- Role changed
224
+ select private.audit_log(
225
+ p_event_type := 'role_changed',
226
+ p_tenant_id := <org_id>,
227
+ p_target_id := <member_user_id>,
228
+ p_payload := jsonb_build_object('from_role', 'member', 'to_role', 'admin')
229
+ );
230
+
231
+ -- super_admin action (chamado pelo trigger gerado por multi-tenant-rls-writer)
232
+ select private.audit_log(
233
+ p_event_type := 'super_admin_action',
234
+ p_tenant_id := <target_org_id>,
235
+ p_payload := jsonb_build_object('table', 'leads', 'op', 'DELETE', 'reason', 'cleanup')
236
+ );
237
+ ```
238
+
239
+ ### Query forensics — investigar incident
240
+
241
+ ```sql
242
+ -- "Quem deletou todos os leads da org X em 2026-04-15?"
243
+ select
244
+ al.created_at,
245
+ al.event_type,
246
+ al.actor_email_hash, -- hash, mas pode rehasher candidates
247
+ al.payload
248
+ from public.audit_logs al
249
+ where al.tenant_id = '<org_id>'
250
+ and al.created_at::date = '2026-04-15'
251
+ and al.event_type in ('super_admin_action', 'data_exported')
252
+ and al.payload->>'table' = 'leads'
253
+ order by al.created_at;
254
+
255
+ -- Match actor por email (forensics) — calcular hash do email candidato
256
+ select * from public.audit_logs
257
+ where actor_email_hash = encode(digest('admin@acme.com', 'sha256'), 'hex')
258
+ and tenant_id = '<org_id>'
259
+ order by created_at desc
260
+ limit 100;
261
+ ```
262
+
263
+ ## Anti-patterns
264
+
265
+ ### Anti-pattern 1: Tabela audit_logs sem REVOKE
266
+
267
+ **Errado:**
268
+ ```sql
269
+ create table public.audit_logs (...);
270
+ alter table public.audit_logs enable row level security;
271
+ -- Sem REVOKE — admin pode delete via UPDATE policy
272
+ ```
273
+
274
+ **Por quê:** atacante ou admin compromised pode `DELETE FROM audit_logs WHERE event_type = 'super_admin_action'` e apagar evidências de acesso indevido.
275
+
276
+ **Certo:** `REVOKE DELETE, UPDATE ON public.audit_logs FROM authenticated, anon;`
277
+
278
+ ### Anti-pattern 2: actor_email/target_email em raw
279
+
280
+ **Errado:**
281
+ ```sql
282
+ create table public.audit_logs (
283
+ ...
284
+ actor_email text, -- raw email
285
+ target_email text -- raw email
286
+ );
287
+ ```
288
+
289
+ **Por quê:** PII em audit log = LGPD violation. DSR de erasure de user X torna-se complexo (precisa anonimizar todas as rows com email do user).
290
+
291
+ **Certo:** SHA-256 hash. Para forensics, rehasher email candidato e buscar match.
292
+
293
+ ### Anti-pattern 3: Retention sem respeitar legal_hold
294
+
295
+ **Errado:**
296
+ ```sql
297
+ delete from public.audit_logs where created_at < now() - interval '30 days';
298
+ -- Sem filter legal_hold
299
+ ```
300
+
301
+ **Por quê:** apaga evidências necessárias para responder DSR LGPD em curso. Legal violation.
302
+
303
+ **Certo:** `... and legal_hold = false`. Quando DSR processada, marcar `legal_hold = false` permite next retention run.
304
+
305
+ ### Anti-pattern 4: tenant_id ausente ou nullable
306
+
307
+ **Errado:**
308
+ ```sql
309
+ tenant_id uuid -- nullable
310
+ ```
311
+
312
+ **Por quê:** queries "todos eventos da org X" precisam filtrar por tenant_id. NULL quebra filter (`tenant_id = $1` exclui NULLs). Index composite (tenant_id, created_at) menos eficaz com NULLs.
313
+
314
+ **Certo:** `tenant_id uuid not null references public.organizations(id) on delete cascade`. NOT NULL + FK garante consistência.
315
+
316
+ ### Anti-pattern 5: Single audit_logs sem partitioning para org grande
317
+
318
+ **Errado:**
319
+ ```sql
320
+ -- Org enterprise com 10M events/ano em tabela única
321
+ ```
322
+
323
+ **Por quê:** vacuum lento, queries lentas, retention DELETE bloqueia outras escritas.
324
+
325
+ **Certo:** LIST partitioning por `tenant_id` (ver [`multi-tenant-performance-scaling`](../multi-tenant-performance-scaling/SKILL.md)). Cada org = partição própria. Retention vira `DROP TABLE <partition>` (instantâneo).
326
+
327
+ ## Ver também
328
+
329
+ - [supabase-cron-queues](../supabase-cron-queues/SKILL.md) — pg_cron pattern usado para retention scheduler (cross-suite)
330
+ - [multi-tenant-performance-scaling](../multi-tenant-performance-scaling/SKILL.md) — partitioning por org_id (REGRA #5)
331
+ - [multi-tenant-rls-hierarchy](../multi-tenant-rls-hierarchy/SKILL.md) — `private.is_super_admin` + super_admin trigger pattern
332
+ - [lgpd-multi-tenant-compliance](../lgpd-multi-tenant-compliance/SKILL.md) — Phase 114, integração com legal_hold
333
+ - [super-admin-platform-pattern](../super-admin-platform-pattern/SKILL.md) — Phase 111, `super_admin_action` event obrigatório
334
+ - [_shared-multi-tenant/glossary.md](../_shared-multi-tenant/glossary.md) — termos `audit log`, `event taxonomy`, `legal hold`, `PII sanitization`
@@ -0,0 +1,300 @@
1
+ ---
2
+ name: b2b-saas-architecture
3
+ description: Use ao desenhar app B2B multi-tenant (org→department→leader→collaborator) com Supabase + React — Single Schema + org_id + RLS é default; JWT minimal (super_admin: bool); 7 tabelas canônicas; slug imutável.
4
+ ---
5
+
6
+ # B2B SaaS Multi-Tenant — Arquitetura Canônica
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill ao desenhar arquitetura de app B2B SaaS multi-tenant em Supabase. Trigger phrases:
11
+
12
+ - "B2B SaaS multi-tenant", "arquitetura multi-tenant", "isolation strategy"
13
+ - "single schema vs schema-per-tenant"
14
+ - "schema canônico organizations", "departments table", "members"
15
+ - "JWT claims multi-tenant", "app_metadata orgs"
16
+ - "slug org imutável", "tenant routing"
17
+
18
+ Esta skill define o **schema canônico** que `multi-tenant-rls-writer` (Phase 108), `org-onboarding-implementer` (Phase 107), `super-admin-implementer` (Phase 111), `audit-log-implementer` (Phase 109), e demais agents da suíte v1.21 consomem como entrada.
19
+
20
+ ## Regras absolutas
21
+
22
+ **REGRA #1 (estratégia default):** **Single Schema + `org_id` + RLS** é o caminho canônico para 90% dos B2B SaaS. Schema-per-tenant é justificado apenas em compliance extremo (saúde/jurídico com auditoria isolacional). Database-per-tenant é inviável economicamente fora de contratos enterprise.
23
+
24
+ **REGRA #2 (JWT minimal):** **APENAS** `super_admin: bool` em `app_metadata`. Lista de orgs no JWT é anti-pattern — bloat linear no token + stale de 1h após mudança de role. O banco (helper functions PG) é fonte de verdade.
25
+
26
+ **REGRA #3 (slug imutável):** `organizations.slug` é **append-only** após criação. Mutação requer tabela `slug_history` + redirect 301 em rotas afetadas. Quebra silenciosa de bookmarks/webhooks/OAuth callbacks é o pior bug que cliente B2B encontra.
27
+
28
+ **REGRA #4 (super_admin via service_role):** `app_metadata.super_admin = true` é setado **APENAS** via `auth.admin.updateUserById()` (service role). Cliente NUNCA consegue mutá-lo (≠ `user_metadata` que é editável).
29
+
30
+ **REGRA #5 (FKs com CASCADE explícito):** Todas as FKs têm `ON DELETE` explícito (CASCADE para entidades dependentes da org, RESTRICT para evitar deleção acidental). Sem default — força decisão consciente.
31
+
32
+ ## Patterns canônicos
33
+
34
+ ### Estratégia de isolation — tabela comparativa
35
+
36
+ | Estratégia | Isolation | Custo ops | Compliance | Quando usar |
37
+ |---|---|---|---|---|
38
+ | **Single Schema + `org_id` + RLS** ⭐ | Lógico (RLS) | Baixo | Padrão B2B SaaS | **DEFAULT 90% dos casos** — Stripe, Linear, Vercel, Notion |
39
+ | Schema-per-tenant | Físico (PG schemas) | Médio (N migrations) | Compliance auditável | Saúde/jurídico/governo com requisito explícito de isolamento |
40
+ | Database-per-tenant | Físico (DB separadas) | Alto (N projects Supabase) | Compliance extremo | Apenas contratos enterprise com SLA de isolamento físico |
41
+
42
+ **Recomendação:** comece sempre com Single Schema. Migração para schema-per-tenant é viável (script de fan-out por org_id). Migração reversa não é.
43
+
44
+ ### Schema canônico — 7 tabelas (DDL completo)
45
+
46
+ ```sql
47
+ -- Ordem de criação respeita dependências FK
48
+
49
+ -- 1. organizations (root tenant)
50
+ create table public.organizations (
51
+ id uuid primary key default gen_random_uuid(),
52
+ name text not null,
53
+ slug text unique not null check (slug ~ '^[a-z0-9-]+$' and length(slug) between 2 and 60),
54
+ owner_id uuid not null references auth.users(id) on delete restrict,
55
+ plan text not null default 'free' check (plan in ('free', 'pro', 'enterprise')),
56
+ status text not null default 'active' check (status in ('active', 'suspended', 'archived')),
57
+ metadata jsonb not null default '{}'::jsonb,
58
+ created_at timestamptz not null default now(),
59
+ updated_at timestamptz not null default now()
60
+ );
61
+
62
+ -- 2. departments (sub-tenant opcional)
63
+ create table public.departments (
64
+ id uuid primary key default gen_random_uuid(),
65
+ org_id uuid not null references public.organizations(id) on delete cascade,
66
+ parent_id uuid references public.departments(id) on delete set null,
67
+ name text not null,
68
+ slug text not null check (slug ~ '^[a-z0-9-]+$'),
69
+ metadata jsonb not null default '{}'::jsonb,
70
+ created_at timestamptz not null default now(),
71
+ updated_at timestamptz not null default now(),
72
+ unique (org_id, slug)
73
+ );
74
+
75
+ -- 3. roles (org-scoped, custom roles permitidos)
76
+ create table public.roles (
77
+ id uuid primary key default gen_random_uuid(),
78
+ org_id uuid not null references public.organizations(id) on delete cascade,
79
+ name text not null check (name ~ '^[a-z_]+$'),
80
+ description text,
81
+ is_built_in boolean not null default false,
82
+ created_at timestamptz not null default now(),
83
+ unique (org_id, name)
84
+ );
85
+
86
+ -- 4. permissions (catálogo global)
87
+ create table public.permissions (
88
+ id uuid primary key default gen_random_uuid(),
89
+ action text not null check (action ~ '^[a-z_]+$'),
90
+ resource text not null check (resource ~ '^[a-z_]+$'),
91
+ description text,
92
+ created_at timestamptz not null default now(),
93
+ unique (action, resource)
94
+ );
95
+
96
+ -- 5. role_permissions (M:N — roles ganham permissions)
97
+ create table public.role_permissions (
98
+ role_id uuid not null references public.roles(id) on delete cascade,
99
+ permission_id uuid not null references public.permissions(id) on delete restrict,
100
+ created_at timestamptz not null default now(),
101
+ primary key (role_id, permission_id)
102
+ );
103
+
104
+ -- 6. organization_members (user ↔ org com role)
105
+ create table public.organization_members (
106
+ id uuid primary key default gen_random_uuid(),
107
+ org_id uuid not null references public.organizations(id) on delete cascade,
108
+ user_id uuid not null references auth.users(id) on delete cascade,
109
+ role_id uuid not null references public.roles(id) on delete restrict,
110
+ status text not null default 'active' check (status in ('active', 'suspended', 'left')),
111
+ joined_at timestamptz not null default now(),
112
+ unique (org_id, user_id)
113
+ );
114
+
115
+ -- 7. department_members (user ↔ dept com role override opcional)
116
+ create table public.department_members (
117
+ id uuid primary key default gen_random_uuid(),
118
+ dept_id uuid not null references public.departments(id) on delete cascade,
119
+ user_id uuid not null references auth.users(id) on delete cascade,
120
+ role_id uuid references public.roles(id) on delete set null, -- NULL herda do organization_members
121
+ is_leader boolean not null default false,
122
+ joined_at timestamptz not null default now(),
123
+ unique (dept_id, user_id)
124
+ );
125
+
126
+ -- Slug history (suporte a redirect 301 quando slug é mutado)
127
+ create table public.organization_slug_history (
128
+ id uuid primary key default gen_random_uuid(),
129
+ org_id uuid not null references public.organizations(id) on delete cascade,
130
+ old_slug text not null,
131
+ new_slug text not null,
132
+ changed_at timestamptz not null default now(),
133
+ unique (old_slug)
134
+ );
135
+ ```
136
+
137
+ ### JWT claims minimal — Custom Access Token Hook
138
+
139
+ ```sql
140
+ -- Hook chamado pelo Supabase Auth a cada token emit
141
+ -- Injeta apenas super_admin no app_metadata
142
+ create or replace function public.custom_access_token_hook(event jsonb)
143
+ returns jsonb
144
+ language plpgsql
145
+ security definer
146
+ set search_path = ''
147
+ as $$
148
+ declare
149
+ claims jsonb;
150
+ is_super boolean;
151
+ begin
152
+ -- Buscar super_admin do app_metadata atual
153
+ select coalesce((raw_app_meta_data->>'super_admin')::boolean, false)
154
+ into is_super
155
+ from auth.users
156
+ where id = (event->>'user_id')::uuid;
157
+
158
+ claims := event->'claims';
159
+ claims := jsonb_set(claims, '{app_metadata}', coalesce(claims->'app_metadata', '{}'::jsonb));
160
+ claims := jsonb_set(claims, '{app_metadata,super_admin}', to_jsonb(is_super));
161
+
162
+ event := jsonb_set(event, '{claims}', claims);
163
+ return event;
164
+ end;
165
+ $$;
166
+
167
+ -- Registrar como hook em supabase/config.toml:
168
+ -- [auth.hook.custom_access_token]
169
+ -- enabled = true
170
+ -- uri = "pg-functions://postgres/public/custom_access_token_hook"
171
+ ```
172
+
173
+ ### Set super_admin via service_role apenas
174
+
175
+ ```typescript
176
+ // Edge Function ou backend admin com service_role key
177
+ import { createClient } from 'jsr:@supabase/supabase-js@2'
178
+
179
+ const admin = createClient(
180
+ Deno.env.get('SUPABASE_URL')!,
181
+ Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! // service role — nunca expor ao client
182
+ )
183
+
184
+ // Promover usuário a super_admin
185
+ await admin.auth.admin.updateUserById(userId, {
186
+ app_metadata: { super_admin: true }
187
+ })
188
+
189
+ // IMPORTANTE: usar updateUserById com app_metadata (não user_metadata)
190
+ ```
191
+
192
+ ### Slug com redirect trail
193
+
194
+ ```sql
195
+ -- Trigger que registra mudança de slug em organization_slug_history
196
+ create or replace function public.track_org_slug_change()
197
+ returns trigger
198
+ language plpgsql
199
+ security invoker
200
+ set search_path = ''
201
+ as $$
202
+ begin
203
+ if old.slug is distinct from new.slug then
204
+ insert into public.organization_slug_history (org_id, old_slug, new_slug)
205
+ values (new.id, old.slug, new.slug);
206
+ end if;
207
+ return new;
208
+ end;
209
+ $$;
210
+
211
+ create trigger track_org_slug_change_trigger
212
+ after update of slug on public.organizations
213
+ for each row execute function public.track_org_slug_change();
214
+ ```
215
+
216
+ App side (Next.js middleware): se `slug` em URL não existe em `organizations`, consulta `organization_slug_history.old_slug` → 301 para `/orgs/{new_slug}/`.
217
+
218
+ ## Anti-patterns
219
+
220
+ ### Anti-pattern 1: Lista de orgs no JWT (claims bloat + stale)
221
+
222
+ **Errado:**
223
+ ```sql
224
+ -- Hook injeta lista de orgs do user no JWT
225
+ -- claims.app_metadata.orgs = [{id, role}, {id, role}, ...]
226
+ ```
227
+
228
+ **Por quê:**
229
+ - JWT tem limite ~4KB — usuário em 50 orgs estoura
230
+ - JWT cacheado por 1h — mudança de role demora até 1h pra propagar
231
+ - Cada Edge Function paga overhead de parsing de claims grandes
232
+
233
+ **Certo:** JWT só com `super_admin: bool`. Helper functions PG (`private.is_member_of`, `private.has_role`) consultam o banco — fonte de verdade sem stale.
234
+
235
+ ### Anti-pattern 2: Slug mutável sem redirect trail
236
+
237
+ **Errado:**
238
+ ```sql
239
+ update public.organizations set slug = 'new-name' where id = '...';
240
+ -- Sem entry em organization_slug_history
241
+ ```
242
+
243
+ **Por quê:** bookmarks externos, webhooks Stripe/Slack, OAuth callbacks, Twitter cards, sitemaps — todos quebram silenciosamente. Cliente B2B descobre semanas depois quando recebe email "seu link parou de funcionar".
244
+
245
+ **Certo:** trigger `track_org_slug_change` automático + middleware redirect 301 em rotas com slug.
246
+
247
+ ### Anti-pattern 3: Schema-per-tenant sem justificativa de compliance
248
+
249
+ **Errado:**
250
+ ```sql
251
+ -- Pra cada nova org, criar schema próprio
252
+ create schema "org_acme";
253
+ create table org_acme.members (...);
254
+ -- ...repetir migration por org
255
+ ```
256
+
257
+ **Por quê:** N migrations por nova org, provisioning lento, queries cross-tenant impossíveis sem UNION manual, monitoring complexo. Sem ganho real para SaaS comum (RLS bem feita já isola).
258
+
259
+ **Certo:** Single Schema + `org_id` + RLS. Schema-per-tenant só quando regulatório exige.
260
+
261
+ ### Anti-pattern 4: `user_metadata` para super_admin
262
+
263
+ **Errado:**
264
+ ```sql
265
+ -- Policy lê super_admin de user_metadata
266
+ using ((auth.jwt()->'user_metadata'->>'super_admin')::boolean = true)
267
+ ```
268
+
269
+ **Por quê:** `user_metadata` é editável pelo cliente via `auth.updateUser({ data: { super_admin: true } })`. Privilege escalation imediato — qualquer usuário se torna super_admin. Documentado em [Supabase Splinter 0015](https://supabase.github.io/splinter/0015_rls_references_user_metadata/).
270
+
271
+ **Certo:** `app_metadata.super_admin` (set apenas via service_role).
272
+
273
+ ### Anti-pattern 5: FKs sem CASCADE/RESTRICT explícito
274
+
275
+ **Errado:**
276
+ ```sql
277
+ references public.organizations(id) -- default ON DELETE NO ACTION
278
+ ```
279
+
280
+ **Por quê:** decisão importante (deletar org com 100k rows? bloquear?) fica implícita. NO ACTION pode falhar deleção sem mensagem clara. Comportamento varia por engine.
281
+
282
+ **Certo:**
283
+ ```sql
284
+ references public.organizations(id) on delete cascade -- entidade dependente
285
+ references auth.users(id) on delete restrict -- prevenir orfão
286
+ references public.roles(id) on delete set null -- preservar histórico
287
+ ```
288
+
289
+ ## Ver também
290
+
291
+ - [supabase-rls-policies](../supabase-rls-policies/SKILL.md) — anti-patterns RLS herdados (`(select auth.uid())` wrapper, no `user_metadata` em authz)
292
+ - [supabase-database-functions](../supabase-database-functions/SKILL.md) — padrões PG functions (security invoker, search_path = '')
293
+ - [supabase-postgres-style](../supabase-postgres-style/SKILL.md) — naming snake_case, lowercase reserved
294
+ - [multi-tenant-rls-hierarchy](../multi-tenant-rls-hierarchy/SKILL.md) — 4 helper functions PG canônicas + policies hierárquicas (Phase 108)
295
+ - [multi-tenant-performance-scaling](../multi-tenant-performance-scaling/SKILL.md) — Supavisor pooling + partitioning por `org_id` + MVs per-tenant (skill irmã)
296
+ - [rbac-permissions-matrix-supabase](../rbac-permissions-matrix-supabase/SKILL.md) — modelagem permissions action × resource × scope (Phase 108)
297
+ - [_shared-supabase/glossary.md](../_shared-supabase/glossary.md) — termos canônicos `app_metadata`, `service_role`
298
+ - [_shared-multi-tenant/glossary.md](../_shared-multi-tenant/glossary.md) — termos novos `tenant`, `org_id`, `super_admin`, `RBAC`
299
+ - [Supabase RLS Best Practices](https://makerkit.dev/blog/tutorials/supabase-rls-best-practices) — fonte external canônica
300
+ - [Custom Access Token Hook — Supabase Docs](https://supabase.com/docs/guides/auth/auth-hooks/custom-access-token-hook)