@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,322 @@
1
+ ---
2
+ name: super-admin-platform-pattern
3
+ description: Use ao implementar plataforma super-admin em B2B SaaS multi-tenant — cross-tenant view sobre todas orgs, impersonation com TTL 30min + reason obrigatório + banner visual, super_admin:bool em app_metadata via service_role apenas, audit obrigatório (BLOCKER) em toda ação super-admin.
4
+ ---
5
+
6
+ # Super-Admin Platform Pattern — B2B SaaS Multi-Tenant
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill ao implementar painel de super-admin (você gerenciando todos tenants). Trigger phrases:
11
+
12
+ - "super admin platform", "platform admin"
13
+ - "cross-tenant view", "list all orgs"
14
+ - "impersonation user", "support takeover"
15
+ - "super_admin app_metadata"
16
+ - "GitHub Enterprise impersonation pattern"
17
+
18
+ ## Regras absolutas
19
+
20
+ **REGRA #1 (audit obrigatório — BLOCKER):** Toda ação super-admin **DEVE** emitir evento `super_admin_action` em `audit_logs`. Sem audit log = ABORT no agent (REGRA do `super-admin-implementer`). Compliance LGPD exige rastreabilidade.
21
+
22
+ **REGRA #2 (impersonation TTL 30min):** Sessão de impersonation expira em **30 minutos** automaticamente. Após TTL, sessão revogada (`signOut` automático), super-admin precisa re-iniciar impersonation. Previne sessão zombie.
23
+
24
+ **REGRA #3 (reason obrigatório):** Impersonation requer campo `reason` text não-vazio (mín 10 chars). Registrado em audit_log para investigação posterior. Sem reason = recusar ação.
25
+
26
+ **REGRA #4 (banner visual obrigatório):** Quando super-admin está impersonando, UI mostra **banner persistente** (top da viewport) com texto "Você está vendo como <user.email> em <org.name>. Clique para sair." Cor de aviso (amarelo/vermelho), z-index máximo, não fechável.
27
+
28
+ **REGRA #5 (super_admin via service_role apenas):** `app_metadata.super_admin = true` é setado **EXCLUSIVAMENTE** via `auth.admin.updateUserById()` com `SUPABASE_SERVICE_ROLE_KEY`. Cliente NUNCA consegue mutar. Endpoint de promoção isolado em Edge Function `verify_jwt: false` ou backend admin separado.
29
+
30
+ **REGRA #6 (delete org requer dupla confirmação):** Deletar uma `organizations` (cascade dropping membros, leads, audit_logs) requer:
31
+ 1. super-admin clica botão delete
32
+ 2. Modal pede org.slug exato + reason text + checkbox "Entendo que isso é irreversível"
33
+ 3. RPC `super_admin_delete_org(p_org_id, p_typed_slug, p_reason)` valida + executa + audit
34
+
35
+ ## Patterns canônicos
36
+
37
+ ### Cross-tenant view — RLS via PERMISSIVE
38
+
39
+ ```sql
40
+ -- Policy permissive em organizations: super_admin vê todas
41
+ create policy "organizations_super_admin_view"
42
+ on public.organizations
43
+ as permissive
44
+ for select
45
+ to authenticated
46
+ using (private.is_super_admin());
47
+
48
+ -- Policy normal (já deve existir): owner/admin vê apenas a própria
49
+ create policy "organizations_select_member"
50
+ on public.organizations
51
+ for select
52
+ to authenticated
53
+ using (private.is_member_of(id));
54
+
55
+ -- Mesma lógica para todas tabelas críticas: leads, organization_members, audit_logs, etc.
56
+ -- Padronizar via agent multi-tenant-rls-writer (Phase 108) com super_admin_bypass=true
57
+ ```
58
+
59
+ ### Frontend — listar todos tenants (super-admin only)
60
+
61
+ ```typescript
62
+ // SuperAdminDashboard.tsx
63
+ import { createClient } from '@/lib/supabase/client'
64
+
65
+ export function SuperAdminDashboard() {
66
+ const supabase = createClient()
67
+ const [orgs, setOrgs] = useState<Org[]>([])
68
+
69
+ useEffect(() => {
70
+ // RLS retorna TODAS orgs porque user é super_admin
71
+ supabase.from('organizations')
72
+ .select('id, name, slug, plan, status, created_at, organization_members(count)')
73
+ .order('created_at', { ascending: false })
74
+ .then(({ data }) => setOrgs(data || []))
75
+ }, [])
76
+
77
+ return (
78
+ <Table>
79
+ {orgs.map(o => (
80
+ <Row key={o.id}>
81
+ <Cell>{o.name}</Cell>
82
+ <Cell>{o.slug}</Cell>
83
+ <Cell>{o.plan}</Cell>
84
+ <Cell>{o.organization_members[0].count}</Cell>
85
+ <Cell>
86
+ <Button onClick={() => startImpersonation(o.id)}>Impersonate</Button>
87
+ </Cell>
88
+ </Row>
89
+ ))}
90
+ </Table>
91
+ )
92
+ }
93
+ ```
94
+
95
+ ### Impersonation — Edge Function com TTL 30min
96
+
97
+ ```typescript
98
+ // supabase/functions/super-admin-impersonate/index.ts
99
+ import { createClient } from 'jsr:@supabase/supabase-js@2'
100
+
101
+ Deno.serve(async (req) => {
102
+ const auth = req.headers.get('Authorization')
103
+ if (!auth) return new Response('unauthorized', { status: 401 })
104
+
105
+ // Validar caller é super_admin via JWT app_metadata
106
+ const supabase = createClient(
107
+ Deno.env.get('SUPABASE_URL')!,
108
+ Deno.env.get('SUPABASE_ANON_KEY')!,
109
+ { global: { headers: { Authorization: auth } } }
110
+ )
111
+
112
+ const { data: { user } } = await supabase.auth.getUser()
113
+ if (!user || !user.app_metadata.super_admin) {
114
+ return new Response('forbidden', { status: 403 })
115
+ }
116
+
117
+ const { target_user_id, target_org_id, reason } = await req.json()
118
+
119
+ // REGRA #3: reason obrigatório (min 10 chars)
120
+ if (!reason || reason.trim().length < 10) {
121
+ return new Response(JSON.stringify({ error: 'reason_required_min_10_chars' }), { status: 400 })
122
+ }
123
+
124
+ // Audit ANTES de criar sessão (se falhar audit, falha a ação)
125
+ await supabase.rpc('audit_log', {
126
+ p_event_type: 'super_admin_action',
127
+ p_tenant_id: target_org_id,
128
+ p_target_id: target_user_id,
129
+ p_target_type: 'user',
130
+ p_payload: {
131
+ action: 'impersonation_started',
132
+ reason,
133
+ session_ttl_minutes: 30
134
+ }
135
+ })
136
+
137
+ // Criar sessão impersonation via service_role
138
+ const admin = createClient(
139
+ Deno.env.get('SUPABASE_URL')!,
140
+ Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! // service role
141
+ )
142
+
143
+ const { data: targetUser } = await admin.auth.admin.getUserById(target_user_id)
144
+ if (!targetUser.user) return new Response('target_not_found', { status: 404 })
145
+
146
+ // Gerar magic link com expiry curto (30min)
147
+ const { data: magicLink } = await admin.auth.admin.generateLink({
148
+ type: 'magiclink',
149
+ email: targetUser.user.email!,
150
+ options: {
151
+ redirectTo: `${Deno.env.get('APP_URL')}/?impersonating=1&original_admin_id=${user.id}`
152
+ }
153
+ })
154
+
155
+ // Cookie marker no client side (banner visual lê isso)
156
+ return new Response(JSON.stringify({
157
+ magic_link: magicLink.properties.action_link,
158
+ expires_at: new Date(Date.now() + 30 * 60 * 1000).toISOString()
159
+ }), {
160
+ headers: { 'Content-Type': 'application/json' }
161
+ })
162
+ })
163
+ ```
164
+
165
+ ### Banner visual de impersonation (React)
166
+
167
+ ```typescript
168
+ // app/components/ImpersonationBanner.tsx
169
+ 'use client'
170
+
171
+ export function ImpersonationBanner() {
172
+ const searchParams = useSearchParams()
173
+ const isImpersonating = searchParams.get('impersonating') === '1'
174
+ const adminId = searchParams.get('original_admin_id')
175
+
176
+ if (!isImpersonating) return null
177
+
178
+ const stopImpersonation = async () => {
179
+ await supabase.auth.signOut()
180
+ window.location.href = `/super-admin?stopped_impersonation=1&original_admin_id=${adminId}`
181
+ }
182
+
183
+ return (
184
+ <div className="fixed top-0 left-0 right-0 z-[9999] bg-yellow-400 text-black px-4 py-2 flex items-center justify-between">
185
+ <span>
186
+ ⚠ Você está vendo como outro usuário (super-admin impersonation).
187
+ Sessão expira em <Countdown to={expiresAt} />.
188
+ </span>
189
+ <Button variant="destructive" size="sm" onClick={stopImpersonation}>
190
+ Parar impersonation
191
+ </Button>
192
+ </div>
193
+ )
194
+ }
195
+ ```
196
+
197
+ ### Promover usuário a super_admin (backend admin script)
198
+
199
+ ```typescript
200
+ // scripts/promote-super-admin.ts (NÃO Edge Function — script administrativo)
201
+ // Executado manualmente OU via CLI admin tool (NÃO frontend)
202
+ import { createClient } from '@supabase/supabase-js'
203
+
204
+ const admin = createClient(
205
+ process.env.SUPABASE_URL!,
206
+ process.env.SUPABASE_SERVICE_ROLE_KEY! // SECRET — NUNCA em frontend
207
+ )
208
+
209
+ await admin.auth.admin.updateUserById(userId, {
210
+ app_metadata: { super_admin: true }
211
+ })
212
+ ```
213
+
214
+ ### RPC delete org com dupla confirmação
215
+
216
+ ```sql
217
+ create or replace function public.super_admin_delete_org(
218
+ p_org_id uuid,
219
+ p_typed_slug text,
220
+ p_reason text
221
+ )
222
+ returns void
223
+ language plpgsql
224
+ security invoker
225
+ set search_path = ''
226
+ as $$
227
+ declare
228
+ v_org_slug text;
229
+ begin
230
+ -- Validar caller é super_admin
231
+ if not private.is_super_admin() then
232
+ raise exception 'forbidden_super_admin_only';
233
+ end if;
234
+
235
+ -- Validar reason
236
+ if length(trim(p_reason)) < 10 then
237
+ raise exception 'reason_required_min_10_chars';
238
+ end if;
239
+
240
+ -- Validar typed_slug bate com slug real (REGRA #6 dupla confirmação)
241
+ select slug into v_org_slug from public.organizations where id = p_org_id;
242
+ if v_org_slug is null then raise exception 'org_not_found'; end if;
243
+ if v_org_slug != p_typed_slug then
244
+ raise exception 'slug_mismatch_confirmation_failed';
245
+ end if;
246
+
247
+ -- Audit ANTES (preserva histórico)
248
+ perform private.audit_log(
249
+ 'super_admin_action',
250
+ p_org_id,
251
+ null, 'org', null,
252
+ jsonb_build_object('action', 'delete_org', 'slug', v_org_slug, 'reason', p_reason)
253
+ );
254
+
255
+ -- Soft delete preferred (status = 'archived'), hard delete se realmente necessário
256
+ update public.organizations set status = 'archived' where id = p_org_id;
257
+
258
+ -- Hard delete (se exigido — descomenta abaixo)
259
+ -- delete from public.organizations where id = p_org_id;
260
+ -- (cascade dropa: organization_members, departments, leads, etc.; audit_logs preservado por design)
261
+ end;
262
+ $$;
263
+
264
+ grant execute on function public.super_admin_delete_org(uuid, text, text) to authenticated;
265
+ ```
266
+
267
+ ## Anti-patterns
268
+
269
+ ### Anti-pattern 1: super_admin sem audit log (BLOCKER)
270
+
271
+ **Errado:**
272
+ ```sql
273
+ -- super_admin policy permite tudo, sem trigger ou helper que registra
274
+ ```
275
+
276
+ **Por quê:** ação super-admin sem rastro = você (operador da plataforma) não consegue investigar incident "quem deletou todos os leads da org X em 03/04?". Compliance LGPD violation.
277
+
278
+ **Certo:** REGRA #1 — toda RPC super-admin chama `private.audit_log` ANTES de operar. Trigger AFTER em tabelas críticas dispara automaticamente para super_admin (gerado pelo `multi-tenant-rls-writer` com `audit_super_admin=true`).
279
+
280
+ ### Anti-pattern 2: Impersonation sem TTL
281
+
282
+ **Errado:**
283
+ ```typescript
284
+ // Sessão impersonation persiste indefinidamente
285
+ await supabase.auth.signInWithPassword({ email: targetUser.email, password: '...' })
286
+ ```
287
+
288
+ **Por quê:** super-admin esquece, sessão fica ativa por dias, usuário target cuja "como" foi assumida nem sabe. Ataque interno trivial.
289
+
290
+ **Certo:** REGRA #2 — magic link com expiry 30min, frontend countdown + auto-logout.
291
+
292
+ ### Anti-pattern 3: super_admin via user_metadata
293
+
294
+ **Errado:**
295
+ ```sql
296
+ -- Policy lê super_admin de user_metadata
297
+ using ((auth.jwt()->'user_metadata'->>'super_admin')::boolean = true)
298
+ ```
299
+
300
+ **Por quê:** `user_metadata` é editável pelo client via `supabase.auth.updateUser({ data: { super_admin: true } })`. Privilege escalation imediato — qualquer usuário se torna super-admin.
301
+
302
+ **Certo:** REGRA #5 — `app_metadata.super_admin` (set apenas via service_role).
303
+
304
+ ### Anti-pattern 4: Delete org sem confirmação dupla
305
+
306
+ **Errado:**
307
+ ```typescript
308
+ <Button onClick={() => deleteOrg(orgId)}>Delete</Button>
309
+ ```
310
+
311
+ **Por quê:** click acidental destrói org com 100k records. Cascade delete = irreversible.
312
+
313
+ **Certo:** REGRA #6 — modal exige typed slug + reason + checkbox + RPC valida tudo server-side. Soft delete preferred.
314
+
315
+ ## Ver também
316
+
317
+ - [audit-log-multi-tenant](../audit-log-multi-tenant/SKILL.md) — Phase 109, audit_logs + event `super_admin_action` (REGRA #1)
318
+ - [multi-tenant-rls-hierarchy](../multi-tenant-rls-hierarchy/SKILL.md) — `private.is_super_admin` + PERMISSIVE policy pattern
319
+ - [b2b-saas-architecture](../b2b-saas-architecture/SKILL.md) — JWT minimal `super_admin: bool` em app_metadata (REGRA #5)
320
+ - [supabase-edge-fn-writer](../../agents/supabase-edge-fn-writer.md) — agent invocado para Edge Function impersonation
321
+ - [_shared-multi-tenant/glossary.md](../_shared-multi-tenant/glossary.md) — termos `super_admin`, `impersonation`, `cross-tenant view`, `platform admin`
322
+ - [GitHub Enterprise — Impersonation](https://docs.github.com/en/enterprise-server@latest/admin/user-management/managing-users-in-your-enterprise/impersonating-a-user) — referência external pattern
@@ -0,0 +1,287 @@
1
+ ---
2
+ name: whatsapp-conversation-state-machine
3
+ description: Use ao modelar conversação WhatsApp como state machine xstate v5 + persistência em PG — estados conversation_started → opted_in → engaged → action_taken → closed, persiste estado em conversations table, integra com lead pipeline.
4
+ ---
5
+
6
+ # WhatsApp Conversation State Machine — xstate v5 + Postgres
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill ao modelar fluxo de conversação WhatsApp em B2B (atendimento, vendas, automação). Trigger phrases:
11
+
12
+ - "WhatsApp conversation state", "fluxo conversa whatsapp"
13
+ - "xstate v5 conversation", "state machine xstate"
14
+ - "conversation persisted Postgres"
15
+ - "conversation handoff bot human"
16
+ - "WhatsApp opt-in opt-out flow"
17
+
18
+ ## Regras absolutas
19
+
20
+ **REGRA #1 (state persistido em PG, não em memória):** State da conversa **SEMPRE** persistido em `conversations.state` (JSONB). Memória in-process = state perdido em restart de Edge Function ou cold start. Conversa multi-turn quebra.
21
+
22
+ **REGRA #2 (state machine declarativo via xstate v5):** Use `setup({...}).createMachine({...})` do xstate v5 para definir transições explicitamente. Substitui `if/else` espalhados por regras de transição auditáveis.
23
+
24
+ **REGRA #3 (transições registradas em audit_log):** Toda transição de state emite evento `custom_conversation_transition` em `audit_logs` com `from_state`, `to_state`, `trigger_message_id`, `org_id`.
25
+
26
+ **REGRA #4 (timeout para abandono):** Conversa em estado intermediário (`waiting_user_reply`) por > 24h transiciona automaticamente para `abandoned` via `pg_cron`. Reset a partir de nova mensagem do user.
27
+
28
+ **REGRA #5 (handoff bot→human explícito):** Transition `bot_handling` → `human_handoff` é evento explícito (palavra-chave do user, escalação automática, ou button click). Marca `assigned_to_user_id` no conversation. Bot para de responder.
29
+
30
+ ## Patterns canônicos
31
+
32
+ ### Tabela `conversations`
33
+
34
+ ```sql
35
+ create table public.conversations (
36
+ id uuid primary key default gen_random_uuid(),
37
+ org_id uuid not null references public.organizations(id) on delete cascade,
38
+ contact_phone text not null,
39
+ contact_name text,
40
+ state text not null default 'started'
41
+ check (state in (
42
+ 'started', -- primeira mensagem inbound recebida
43
+ 'opted_in', -- user explicitamente opt-in para automação
44
+ 'engaged', -- user respondeu pelo menos 1×
45
+ 'bot_handling', -- bot está processando
46
+ 'waiting_user_reply',-- bot enviou pergunta, espera resposta
47
+ 'human_handoff', -- atendente humano assumiu
48
+ 'action_taken', -- conversa gerou ação (lead criado, agendamento, etc.)
49
+ 'abandoned', -- timeout 24h sem resposta
50
+ 'closed' -- conversa explicitamente encerrada
51
+ )),
52
+ state_data jsonb default '{}'::jsonb, -- xstate context (variables, accumulated data)
53
+ assigned_to_user_id uuid references auth.users(id) on delete set null,
54
+ lead_id uuid, -- FK lazy para leads (criado on action_taken)
55
+ started_at timestamptz not null default now(),
56
+ last_message_at timestamptz not null default now(),
57
+ closed_at timestamptz,
58
+ unique (org_id, contact_phone, started_at)
59
+ );
60
+
61
+ create index conversations_org_phone_idx on public.conversations (org_id, contact_phone);
62
+ create index conversations_state_idx on public.conversations (state) where state in ('waiting_user_reply', 'bot_handling');
63
+ create index conversations_assigned_idx on public.conversations (assigned_to_user_id) where assigned_to_user_id is not null;
64
+ ```
65
+
66
+ ### State machine em xstate v5 (Edge Function)
67
+
68
+ ```typescript
69
+ // supabase/functions/whatsapp-process/conversation-machine.ts
70
+ import { setup, createActor } from 'jsr:xstate@5'
71
+
72
+ export const conversationMachine = setup({
73
+ types: {
74
+ context: {} as {
75
+ orgId: string
76
+ contactPhone: string
77
+ contactName?: string
78
+ lastMessageContent?: string
79
+ collectedFields: Record<string, string>
80
+ },
81
+ events: {} as
82
+ | { type: 'INBOUND_MESSAGE'; content: string }
83
+ | { type: 'OPT_IN_KEYWORD' }
84
+ | { type: 'OPT_OUT_KEYWORD' }
85
+ | { type: 'BOT_REPLIED'; question?: string }
86
+ | { type: 'USER_REPLIED'; content: string }
87
+ | { type: 'HUMAN_HANDOFF_REQUESTED'; reason?: string }
88
+ | { type: 'ACTION_TAKEN'; actionType: string; leadId?: string }
89
+ | { type: 'TIMEOUT_24H' }
90
+ | { type: 'CLOSE' }
91
+ },
92
+ guards: {
93
+ isOptInKeyword: ({ event }) => {
94
+ if (event.type !== 'INBOUND_MESSAGE') return false
95
+ return /^(sim|aceito|comecar|start|opt-in)$/i.test(event.content.trim())
96
+ },
97
+ isOptOutKeyword: ({ event }) => {
98
+ if (event.type !== 'INBOUND_MESSAGE') return false
99
+ return /^(nao|sair|stop|opt-out|cancelar)$/i.test(event.content.trim())
100
+ },
101
+ isHandoffKeyword: ({ event }) => {
102
+ if (event.type !== 'INBOUND_MESSAGE') return false
103
+ return /(atendente|humano|falar com pessoa|human)/i.test(event.content)
104
+ }
105
+ }
106
+ }).createMachine({
107
+ id: 'conversation',
108
+ initial: 'started',
109
+ context: ({ input }) => input,
110
+ states: {
111
+ started: {
112
+ on: {
113
+ INBOUND_MESSAGE: [
114
+ { guard: 'isOptInKeyword', target: 'opted_in' },
115
+ { guard: 'isHandoffKeyword', target: 'human_handoff' },
116
+ { target: 'engaged' }
117
+ ]
118
+ }
119
+ },
120
+ opted_in: {
121
+ on: {
122
+ BOT_REPLIED: { target: 'waiting_user_reply' }
123
+ }
124
+ },
125
+ engaged: {
126
+ on: {
127
+ BOT_REPLIED: { target: 'waiting_user_reply' },
128
+ HUMAN_HANDOFF_REQUESTED: { target: 'human_handoff' }
129
+ }
130
+ },
131
+ waiting_user_reply: {
132
+ on: {
133
+ USER_REPLIED: { target: 'bot_handling' },
134
+ TIMEOUT_24H: { target: 'abandoned' },
135
+ HUMAN_HANDOFF_REQUESTED: { target: 'human_handoff' }
136
+ }
137
+ },
138
+ bot_handling: {
139
+ on: {
140
+ BOT_REPLIED: { target: 'waiting_user_reply' },
141
+ ACTION_TAKEN: { target: 'action_taken' },
142
+ HUMAN_HANDOFF_REQUESTED: { target: 'human_handoff' }
143
+ }
144
+ },
145
+ human_handoff: {
146
+ on: {
147
+ ACTION_TAKEN: { target: 'action_taken' },
148
+ CLOSE: { target: 'closed' }
149
+ }
150
+ },
151
+ action_taken: {
152
+ on: {
153
+ INBOUND_MESSAGE: { target: 'engaged' },
154
+ CLOSE: { target: 'closed' }
155
+ }
156
+ },
157
+ abandoned: {
158
+ on: {
159
+ INBOUND_MESSAGE: { target: 'engaged' } // re-engaja
160
+ }
161
+ },
162
+ closed: {
163
+ type: 'final'
164
+ }
165
+ }
166
+ })
167
+ ```
168
+
169
+ ### Persistência — load + transition + save
170
+
171
+ ```typescript
172
+ // PT-BR: hydrate state machine do PG, processa evento, persiste novo state
173
+ async function processConversationEvent(orgId: string, contactPhone: string, event: ConversationEvent) {
174
+ // 1. Load existing conversation
175
+ const { data: conv } = await admin
176
+ .from('conversations')
177
+ .select('*')
178
+ .eq('org_id', orgId)
179
+ .eq('contact_phone', contactPhone)
180
+ .order('started_at', { ascending: false })
181
+ .limit(1)
182
+ .single()
183
+
184
+ // 2. Hydrate xstate actor com state persistido
185
+ const actor = createActor(conversationMachine, {
186
+ input: { orgId, contactPhone, contactName: conv?.contact_name, collectedFields: conv?.state_data || {} },
187
+ snapshot: conv ? { value: conv.state, context: conv.state_data, status: 'active' } : undefined
188
+ })
189
+ actor.start()
190
+
191
+ // 3. Send event
192
+ actor.send(event)
193
+
194
+ // 4. Get new state
195
+ const snapshot = actor.getSnapshot()
196
+ const newState = snapshot.value as string
197
+
198
+ // 5. Persist (REGRA #1)
199
+ await admin.from('conversations').upsert({
200
+ id: conv?.id,
201
+ org_id: orgId,
202
+ contact_phone: contactPhone,
203
+ state: newState,
204
+ state_data: snapshot.context,
205
+ last_message_at: new Date().toISOString()
206
+ })
207
+
208
+ // 6. Audit transition (REGRA #3)
209
+ if (conv?.state !== newState) {
210
+ await admin.rpc('audit_log', {
211
+ p_event_type: 'custom_conversation_transition',
212
+ p_tenant_id: orgId,
213
+ p_payload: {
214
+ from_state: conv?.state,
215
+ to_state: newState,
216
+ contact_phone: contactPhone
217
+ }
218
+ })
219
+ }
220
+
221
+ return snapshot
222
+ }
223
+ ```
224
+
225
+ ### Cron timeout 24h (REGRA #4)
226
+
227
+ ```sql
228
+ select cron.schedule(
229
+ 'conversation-timeout-24h',
230
+ '*/30 * * * *', -- a cada 30min
231
+ $$
232
+ update public.conversations
233
+ set state = 'abandoned',
234
+ last_message_at = now()
235
+ where state = 'waiting_user_reply'
236
+ and last_message_at < now() - interval '24 hours';
237
+ $$
238
+ );
239
+ ```
240
+
241
+ ## Anti-patterns
242
+
243
+ ### Anti-pattern 1: State em variável global da Edge Function
244
+
245
+ **Errado:**
246
+ ```typescript
247
+ const conversationStates = new Map<string, string>() // in-memory
248
+ ```
249
+
250
+ **Por quê:** Edge Function reinicia (cold start) → Map vazio → conversa perdida.
251
+
252
+ **Certo:** REGRA #1 — `conversations.state` em PG, hydrate xstate actor com snapshot.
253
+
254
+ ### Anti-pattern 2: if/else aninhado em vez de state machine
255
+
256
+ **Errado:**
257
+ ```typescript
258
+ if (conv.state === 'started') {
259
+ if (msg.includes('sim')) { ... }
260
+ else if (msg.includes('atendente')) { ... }
261
+ // 50 lines of nested ifs
262
+ }
263
+ ```
264
+
265
+ **Por quê:** transições implícitas, hard to audit, bug silencioso quando adiciona novo estado.
266
+
267
+ **Certo:** REGRA #2 — xstate `setup({...}).createMachine({...})` declarativo.
268
+
269
+ ### Anti-pattern 3: Sem timeout para abandono
270
+
271
+ **Errado:**
272
+ ```sql
273
+ -- Nenhum cron — conversas em waiting_user_reply ficam para sempre
274
+ ```
275
+
276
+ **Por quê:** dashboards lotados de conversas "abertas" que na verdade abandonadas. Métricas de conversion erradas.
277
+
278
+ **Certo:** REGRA #4 — pg_cron 30min checa timeout 24h.
279
+
280
+ ## Ver também
281
+
282
+ - [evolution-go-whatsapp-integration](../evolution-go-whatsapp-integration/SKILL.md) — sibling, webhook handler integra com state machine
283
+ - [crm-lead-pipeline-patterns](../crm-lead-pipeline-patterns/SKILL.md) — Phase 113, conversa.action_taken → lead criado
284
+ - [audit-log-multi-tenant](../audit-log-multi-tenant/SKILL.md) — eventos `custom_conversation_transition`
285
+ - [supabase-cron-queues](../supabase-cron-queues/SKILL.md) — pg_cron timeout 24h
286
+ - [_shared-multi-tenant/glossary.md](../_shared-multi-tenant/glossary.md) — `conversation state machine`
287
+ - [xstate v5 docs](https://stately.ai/docs/xstate) — biblioteca canônica