@luanpdd/kit-mcp 1.19.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 (35) hide show
  1. package/README.md +1 -1
  2. package/gates/dept-cycle-prevention.md +179 -0
  3. package/gates/multi-tenant-rls-coverage.md +102 -0
  4. package/gates/service-role-not-in-user-facing.md +113 -0
  5. package/kit/agents/audit-log-implementer.md +175 -0
  6. package/kit/agents/b2b-saas-architect.md +156 -0
  7. package/kit/agents/crm-pipeline-implementer.md +150 -0
  8. package/kit/agents/evolution-go-integrator.md +179 -0
  9. package/kit/agents/invite-flow-implementer.md +137 -0
  10. package/kit/agents/lgpd-compliance-auditor.md +206 -0
  11. package/kit/agents/multi-tenant-isolation-auditor.md +243 -0
  12. package/kit/agents/multi-tenant-rls-writer.md +262 -0
  13. package/kit/agents/org-onboarding-implementer.md +202 -0
  14. package/kit/agents/super-admin-implementer.md +182 -0
  15. package/kit/commands/burn-rate-status.md +237 -121
  16. package/kit/commands/multi-tenant.md +163 -0
  17. package/kit/file-manifest.json +31 -4
  18. package/kit/skills/_shared-multi-tenant/glossary.md +186 -0
  19. package/kit/skills/audit-log-multi-tenant/SKILL.md +334 -0
  20. package/kit/skills/b2b-saas-architecture/SKILL.md +300 -0
  21. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +326 -0
  22. package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -0
  23. package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -0
  24. package/kit/skills/member-invite-flow/SKILL.md +305 -0
  25. package/kit/skills/member-management-react-shadcn/SKILL.md +328 -0
  26. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +312 -0
  27. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +338 -0
  28. package/kit/skills/org-onboarding-flow/SKILL.md +257 -0
  29. package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -0
  30. package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -0
  31. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +301 -0
  32. package/kit/skills/super-admin-platform-pattern/SKILL.md +322 -0
  33. package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -0
  34. package/package.json +6 -2
  35. package/src/mcp-server/index.js +34 -3
package/README.md CHANGED
@@ -24,7 +24,7 @@ Inspired by [vinilana/dotcontext](https://github.com/vinilana/dotcontext) — se
24
24
  ---
25
25
 
26
26
  <!-- AUTOGEN-COUNTS-START -->
27
- **Bundled workflow:** 47 agents · 87 commands · 45 skills · 20 gates
27
+ **Bundled workflow:** 57 agents · 88 commands · 60 skills · 23 gates
28
28
  <!-- AUTOGEN-COUNTS-END -->
29
29
 
30
30
  ## What ships in the box
@@ -0,0 +1,179 @@
1
+ ---
2
+ id: dept-cycle-prevention
3
+ stage: pre-verify
4
+ blocking: true
5
+ description: Detecta tabela departments (ou similar) com parent_id FK self-referencial mas sem trigger anti-cycle. Loop circular em dept hierarchy esgota connection pool via WITH RECURSIVE infinito. Skip se projeto não tem supabase/migrations/.
6
+ ---
7
+
8
+ # Department Cycle Prevention gate
9
+
10
+ **When to run:** pre-verify (blocking — anti-pitfall P0 multi-tenant hierarchy).
11
+
12
+ ## Check
13
+
14
+ ```bash
15
+ #!/usr/bin/env bash
16
+ # PT-BR: detecta departments com parent_id self-referencial sem trigger anti-cycle.
17
+ # Anti-pitfall #3 multi-tenant: dept hierarchy com loop circular (A.parent=B, B.parent=A) → WITH RECURSIVE infinito → connection pool exhaustion.
18
+ # Bash 3.2-portable (macOS default).
19
+ set -e
20
+
21
+ MIGRATIONS_DIR="supabase/migrations"
22
+
23
+ if [ ! -d "$MIGRATIONS_DIR" ]; then
24
+ echo "INFO: $MIGRATIONS_DIR não existe — projeto não usa Supabase migrations. Gate skipped."
25
+ exit 0
26
+ fi
27
+
28
+ # PT-BR: nomes comuns de tabelas hierárquicas (departments + variantes)
29
+ HIERARCHY_TABLE_PATTERNS="departments|teams|groups|categories|nodes|tree"
30
+
31
+ VIOLATIONS=0
32
+ VIOLATIONS_DETAIL=""
33
+
34
+ # PT-BR: iterar migrations em ordem cronológica
35
+ MIGRATION_FILES=$(ls "$MIGRATIONS_DIR"/*.sql 2>/dev/null | sort)
36
+
37
+ if [ -z "$MIGRATION_FILES" ]; then
38
+ echo "INFO: nenhum arquivo .sql em $MIGRATIONS_DIR — gate skipped."
39
+ exit 0
40
+ fi
41
+
42
+ # PT-BR: encontrar tabelas com parent_id auto-referencial (heurística: parent_id + REFERENCES <self>)
43
+ SELF_REF_TABLES=""
44
+
45
+ for f in $MIGRATION_FILES; do
46
+ # PT-BR: detecta padrão "parent_id ... references public.<table>(id)" onde <table> bate com nome da tabela sendo criada
47
+ # (case-insensitive, multi-line awk porque DDL pode ter quebras de linha)
48
+ TABLES_IN_FILE=$(awk '
49
+ BEGIN { in_create = 0; current_table = "" }
50
+ /create[ \t]+table[ \t]+(if[ \t]+not[ \t]+exists[ \t]+)?[a-z_]+\.[a-z_]+/ {
51
+ in_create = 1
52
+ match($0, /create[ \t]+table[ \t]+(if[ \t]+not[ \t]+exists[ \t]+)?([a-z_]+\.[a-z_]+)/)
53
+ if (RSTART > 0) {
54
+ # extrair só o último match group (table name)
55
+ s = substr($0, RSTART, RLENGTH)
56
+ n = split(s, parts, /[ \t]+/)
57
+ current_table = parts[n]
58
+ }
59
+ next
60
+ }
61
+ in_create && /parent_id[ \t]+uuid[ \t]+references[ \t]+([a-z_]+\.[a-z_]+)/ {
62
+ match($0, /references[ \t]+([a-z_]+\.[a-z_]+)/)
63
+ if (RSTART > 0) {
64
+ ref_table = substr($0, RSTART + 11, RLENGTH - 11)
65
+ gsub(/[ \t]/, "", ref_table)
66
+ if (ref_table == current_table) {
67
+ print current_table
68
+ }
69
+ }
70
+ }
71
+ /;[ \t]*$/ && in_create { in_create = 0; current_table = "" }
72
+ ' "$f" 2>/dev/null)
73
+
74
+ if [ -n "$TABLES_IN_FILE" ]; then
75
+ SELF_REF_TABLES="$SELF_REF_TABLES
76
+ $TABLES_IN_FILE"
77
+ fi
78
+ done
79
+
80
+ # PT-BR: filtrar entries vazias
81
+ SELF_REF_TABLES=$(echo "$SELF_REF_TABLES" | grep -v "^$" | sort -u)
82
+
83
+ if [ -z "$SELF_REF_TABLES" ]; then
84
+ echo "INFO: nenhuma tabela com parent_id self-referencial detectada — gate skipped."
85
+ exit 0
86
+ fi
87
+
88
+ # PT-BR: para cada tabela self-ref, verificar se há trigger ou function anti-cycle
89
+ for table in $SELF_REF_TABLES; do
90
+ # PT-BR: extrair só nome da tabela (sem schema)
91
+ table_name=$(echo "$table" | awk -F. '{print $2}')
92
+
93
+ # PT-BR: heurística: procurar trigger com nome contendo cycle/loop/recursion + tabela
94
+ CYCLE_GUARD_FOUND=0
95
+ for f in $MIGRATION_FILES; do
96
+ if grep -iE "(create[ \t]+(or[ \t]+replace[ \t]+)?(function|trigger))[ \t]+[a-z_]*(cycle|loop|recurs|hierarchy_check|anti_cycle)" "$f" 2>/dev/null \
97
+ | grep -iqE "$table_name|$(echo "$table" | tr '.' '_')"; then
98
+ CYCLE_GUARD_FOUND=1
99
+ break
100
+ fi
101
+
102
+ # PT-BR: pattern alternativo: trigger genérico que menciona a tabela e WITH RECURSIVE em corpo
103
+ if grep -iqE "create[ \t]+trigger.*on[ \t]+$table" "$f" 2>/dev/null \
104
+ && grep -iqE "with[ \t]+recursive.*parent_id" "$f" 2>/dev/null; then
105
+ CYCLE_GUARD_FOUND=1
106
+ break
107
+ fi
108
+ done
109
+
110
+ if [ "$CYCLE_GUARD_FOUND" -eq 0 ]; then
111
+ VIOLATIONS=$((VIOLATIONS + 1))
112
+ VIOLATIONS_DETAIL="${VIOLATIONS_DETAIL}
113
+ Tabela '$table' tem parent_id self-referencial mas sem trigger anti-cycle detectado"
114
+ fi
115
+ done
116
+
117
+ if [ "$VIOLATIONS" -eq 0 ]; then
118
+ echo "PASS: todas as tabelas hierárquicas têm proteção anti-cycle."
119
+ exit 0
120
+ else
121
+ echo "FAIL: $VIOLATIONS tabela(s) hierárquica(s) sem trigger anti-cycle:$VIOLATIONS_DETAIL"
122
+ echo ""
123
+ echo "Fix: adicionar trigger BEFORE INSERT/UPDATE que detecta cycle via WITH RECURSIVE:"
124
+ cat << 'EOF'
125
+
126
+ create or replace function private.check_no_dept_cycle()
127
+ returns trigger
128
+ language plpgsql
129
+ security invoker
130
+ set search_path = ''
131
+ as $$
132
+ declare
133
+ cycle_detected boolean;
134
+ begin
135
+ if new.parent_id is null then return new; end if;
136
+
137
+ with recursive ancestors as (
138
+ select id, parent_id, 1 as depth from public.departments where id = new.parent_id
139
+ union all
140
+ select d.id, d.parent_id, a.depth + 1
141
+ from public.departments d
142
+ join ancestors a on d.id = a.parent_id
143
+ where a.depth < 10 -- max 10 níveis
144
+ )
145
+ select exists (select 1 from ancestors where id = new.id) into cycle_detected;
146
+
147
+ if cycle_detected then
148
+ raise exception 'department hierarchy cycle detected: % cannot be parent of %',
149
+ new.parent_id, new.id;
150
+ end if;
151
+
152
+ return new;
153
+ end;
154
+ $$;
155
+
156
+ create trigger check_no_dept_cycle_trigger
157
+ before insert or update of parent_id on public.departments
158
+ for each row execute function private.check_no_dept_cycle();
159
+
160
+ EOF
161
+ echo "Ref: kit/skills/_shared-multi-tenant/glossary.md (Department Hierarchy)"
162
+ exit 1
163
+ fi
164
+ ```
165
+
166
+ ## Verdict
167
+
168
+ - **passed** — todas tabelas hierárquicas têm proteção anti-cycle → continuar
169
+ - **block** — apresentar tabelas violadoras + DDL pronto do trigger anti-cycle
170
+
171
+ ## Notes
172
+
173
+ Detecção é heurística baseada em naming (`parent_id` + `references <self_table>`). Pode produzir:
174
+ - **Falso-negativo** se a coluna se chamar `parent` em vez de `parent_id`, ou referenciar tabela diferente (não self-ref)
175
+ - **Falso-positivo** se o nome do trigger não contém palavras-chave (`cycle`, `loop`, `recurs`, `anti_cycle`) — neste caso, renomear o trigger ou estender a allowlist do gate
176
+
177
+ Tabelas hierárquicas tipicamente afetadas: `departments`, `teams`, `groups`, `categories`, `nodes`, `tree`.
178
+
179
+ Limit recursivo padrão sugerido: 10 níveis. Hierarquia mais profunda que isso indica modelagem questionável (Linear/Notion suportam até 5 níveis).
@@ -0,0 +1,102 @@
1
+ ---
2
+ id: multi-tenant-rls-coverage
3
+ stage: pre-verify
4
+ blocking: true
5
+ description: Detecta CREATE TABLE em supabase/migrations/ sem ENABLE ROW LEVEL SECURITY no mesmo arquivo. Cross-tenant data leak silencioso é a falha #1 de apps multi-tenant Supabase. Skip se projeto não tem supabase/migrations/.
6
+ ---
7
+
8
+ # Multi-Tenant RLS Coverage gate
9
+
10
+ **When to run:** pre-verify (blocking — multi-tenant phase não verifica até cobertura completa).
11
+
12
+ ## Check
13
+
14
+ ```bash
15
+ #!/usr/bin/env bash
16
+ # PT-BR: detecta CREATE TABLE em supabase/migrations/ sem ENABLE ROW LEVEL SECURITY no mesmo arquivo.
17
+ # Anti-pitfall #1 multi-tenant: tabela nova sem RLS = cross-tenant leak silencioso (Postgres não aplica policies automaticamente).
18
+ # Bash 3.2-portable (macOS default).
19
+ set -e
20
+
21
+ MIGRATIONS_DIR="supabase/migrations"
22
+
23
+ # PT-BR: skip gracioso se projeto não tem migrations Supabase
24
+ if [ ! -d "$MIGRATIONS_DIR" ]; then
25
+ echo "INFO: $MIGRATIONS_DIR não existe — projeto não usa Supabase migrations. Gate skipped."
26
+ exit 0
27
+ fi
28
+
29
+ # PT-BR: tabelas em schemas system não exigem RLS (auth, storage, realtime, vault, supabase_*)
30
+ SYSTEM_SCHEMA_PREFIXES="auth\\.|storage\\.|realtime\\.|vault\\.|supabase_|extensions\\."
31
+
32
+ # PT-BR: allowlist de tabelas que conscientemente não têm RLS (ex: lookup tables públicas)
33
+ ALLOWLIST_TABLES=(
34
+ "public.permissions" # catálogo global de permissions, leitura pública por design
35
+ )
36
+
37
+ is_allowlisted() {
38
+ local table="$1"
39
+ for at in "${ALLOWLIST_TABLES[@]}"; do
40
+ [ "$table" = "$at" ] && return 0
41
+ done
42
+ return 1
43
+ }
44
+
45
+ VIOLATIONS=0
46
+ VIOLATIONS_DETAIL=""
47
+
48
+ # PT-BR: iterar migrations em ordem cronológica
49
+ MIGRATION_FILES=$(ls "$MIGRATIONS_DIR"/*.sql 2>/dev/null | sort)
50
+
51
+ if [ -z "$MIGRATION_FILES" ]; then
52
+ echo "INFO: nenhum arquivo .sql em $MIGRATIONS_DIR — gate skipped."
53
+ exit 0
54
+ fi
55
+
56
+ for f in $MIGRATION_FILES; do
57
+ # PT-BR: extrair tabelas criadas via CREATE TABLE (case-insensitive, ignora IF NOT EXISTS)
58
+ CREATED_TABLES=$(grep -iE "^create\s+table\s+(if\s+not\s+exists\s+)?[a-z_]+\." "$f" 2>/dev/null \
59
+ | sed -E 's/.*create\s+table\s+(if\s+not\s+exists\s+)?([a-z_]+\.[a-z_]+).*/\2/i' \
60
+ | grep -viE "$SYSTEM_SCHEMA_PREFIXES" || true)
61
+
62
+ # PT-BR: extrair tabelas com RLS habilitada no MESMO arquivo
63
+ RLS_TABLES=$(grep -iE "alter\s+table\s+[a-z_]+\.[a-z_]+\s+enable\s+row\s+level\s+security" "$f" 2>/dev/null \
64
+ | sed -E 's/.*alter\s+table\s+([a-z_]+\.[a-z_]+)\s+enable.*/\1/i' || true)
65
+
66
+ # PT-BR: para cada tabela criada, checar se RLS foi habilitada
67
+ for table in $CREATED_TABLES; do
68
+ [ -z "$table" ] && continue
69
+ is_allowlisted "$table" && continue
70
+
71
+ if ! echo "$RLS_TABLES" | grep -qFx "$table"; then
72
+ VIOLATIONS=$((VIOLATIONS + 1))
73
+ VIOLATIONS_DETAIL="${VIOLATIONS_DETAIL}
74
+ $(basename "$f"): tabela '$table' criada sem ENABLE ROW LEVEL SECURITY"
75
+ fi
76
+ done
77
+ done
78
+
79
+ if [ "$VIOLATIONS" -eq 0 ]; then
80
+ echo "PASS: todas as tabelas em supabase/migrations/ têm RLS habilitada no mesmo arquivo de criação."
81
+ exit 0
82
+ else
83
+ echo "FAIL: $VIOLATIONS tabela(s) criada(s) sem ENABLE ROW LEVEL SECURITY:$VIOLATIONS_DETAIL"
84
+ echo ""
85
+ echo "Fix: adicione 'alter table <schema>.<table> enable row level security;' no MESMO arquivo de migration que criou a tabela."
86
+ echo "Ref: kit/skills/multi-tenant-rls-hierarchy/SKILL.md (REGRA #1)"
87
+ exit 1
88
+ fi
89
+ ```
90
+
91
+ ## Verdict
92
+
93
+ - **passed** — todas tabelas multi-tenant têm RLS habilitada → continuar
94
+ - **block** — apresentar tabela de violations + sugestão de fix; sem opção de skip (anti-pitfall P0 — cross-tenant leak)
95
+
96
+ ## Notes
97
+
98
+ Este gate só checa **habilitação** de RLS — não checa se as policies cobrem todos os casos. Ver `multi-tenant-isolation-auditor` agent para análise completa de policies (requer MCP Supabase ativo para query a `pg_policies`).
99
+
100
+ Tabelas em schemas system (`auth.*`, `storage.*`, `realtime.*`, `vault.*`, `supabase_*`, `extensions.*`) são automaticamente skipped — Supabase já aplica RLS interno nelas.
101
+
102
+ Allowlist mínima: `public.permissions` (catálogo global de permissions, leitura pública por design — tem `to authenticated` em SELECT mas sem isolamento por tenant).
@@ -0,0 +1,113 @@
1
+ ---
2
+ id: service-role-not-in-user-facing
3
+ stage: pre-verify
4
+ blocking: true
5
+ description: Detecta uso de SUPABASE_SERVICE_ROLE_KEY em Edge Functions com verify_jwt:true (user-facing). Service role bypassa RLS — uso em rota acessível a usuário desliga toda autorização. Skip se projeto não tem supabase/functions/.
6
+ ---
7
+
8
+ # Service Role Not In User-Facing gate
9
+
10
+ **When to run:** pre-verify (blocking — anti-pitfall P0 multi-tenant).
11
+
12
+ ## Check
13
+
14
+ ```bash
15
+ #!/usr/bin/env bash
16
+ # PT-BR: detecta uso de SUPABASE_SERVICE_ROLE_KEY em Edge Functions com verify_jwt:true.
17
+ # Anti-pitfall #2 multi-tenant: service role em rota user-facing = bypass total de RLS.
18
+ # Bash 3.2-portable (macOS default).
19
+ set -e
20
+
21
+ FUNCTIONS_DIR="supabase/functions"
22
+ CONFIG_FILE="supabase/config.toml"
23
+
24
+ if [ ! -d "$FUNCTIONS_DIR" ]; then
25
+ echo "INFO: $FUNCTIONS_DIR não existe — projeto não usa Supabase Edge Functions. Gate skipped."
26
+ exit 0
27
+ fi
28
+
29
+ # PT-BR: env vars que indicam uso de service role
30
+ SERVICE_ROLE_PATTERNS="SUPABASE_SERVICE_ROLE_KEY|SERVICE_ROLE_KEY|service_role_key|serviceRoleKey"
31
+
32
+ VIOLATIONS=0
33
+ VIOLATIONS_DETAIL=""
34
+
35
+ # PT-BR: iterar cada Edge Function (cada subdir em functions/)
36
+ FUNCTION_DIRS=$(find "$FUNCTIONS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
37
+
38
+ if [ -z "$FUNCTION_DIRS" ]; then
39
+ echo "INFO: nenhuma Edge Function em $FUNCTIONS_DIR — gate skipped."
40
+ exit 0
41
+ fi
42
+
43
+ for fn_dir in $FUNCTION_DIRS; do
44
+ fn_name=$(basename "$fn_dir")
45
+
46
+ # PT-BR: skip _shared (não é Edge Function deployable)
47
+ [ "$fn_name" = "_shared" ] && continue
48
+
49
+ # PT-BR: descobrir verify_jwt setting da função (default: true se ausente)
50
+ VERIFY_JWT="true" # default Supabase
51
+ if [ -f "$CONFIG_FILE" ]; then
52
+ # PT-BR: parsing leve — procurar [functions.<name>] block + verify_jwt
53
+ SECTION_VERIFY=$(awk -v fn="$fn_name" '
54
+ $0 ~ "^\\[functions\\." fn "\\]" { in_section = 1; next }
55
+ in_section && /^\[/ { in_section = 0 }
56
+ in_section && /verify_jwt/ {
57
+ gsub(/[ \t]/, "")
58
+ split($0, a, "=")
59
+ print a[2]
60
+ exit
61
+ }
62
+ ' "$CONFIG_FILE" 2>/dev/null)
63
+
64
+ if [ -n "$SECTION_VERIFY" ]; then
65
+ VERIFY_JWT="$SECTION_VERIFY"
66
+ fi
67
+ fi
68
+
69
+ # PT-BR: se verify_jwt=false (webhook/internal), skip — service role é OK aqui
70
+ if [ "$VERIFY_JWT" = "false" ]; then
71
+ continue
72
+ fi
73
+
74
+ # PT-BR: buscar uso de service role em arquivos .ts/.js da function
75
+ SERVICE_ROLE_FILES=$(grep -rlE "$SERVICE_ROLE_PATTERNS" "$fn_dir" --include="*.ts" --include="*.js" --include="*.mjs" 2>/dev/null || true)
76
+
77
+ if [ -n "$SERVICE_ROLE_FILES" ]; then
78
+ VIOLATIONS=$((VIOLATIONS + 1))
79
+ VIOLATIONS_DETAIL="${VIOLATIONS_DETAIL}
80
+ Edge Function '$fn_name' (verify_jwt=$VERIFY_JWT) usa service role:
81
+ $(echo "$SERVICE_ROLE_FILES" | sed 's/^/ /')"
82
+ fi
83
+ done
84
+
85
+ if [ "$VIOLATIONS" -eq 0 ]; then
86
+ echo "PASS: nenhuma Edge Function user-facing (verify_jwt=true) usa SERVICE_ROLE_KEY."
87
+ exit 0
88
+ else
89
+ echo "FAIL: $VIOLATIONS Edge Function(s) user-facing usam SERVICE_ROLE_KEY:$VIOLATIONS_DETAIL"
90
+ echo ""
91
+ echo "Fix: use ANON_KEY com JWT do user para preservar RLS. Se realmente precisa de service role:"
92
+ echo " - Mover lógica privilegiada para Edge Function separada com verify_jwt=false (webhook ou cron-only)"
93
+ echo " - OU validar manualmente quem está chamando antes de usar service role (anti-pattern, mas explícito)"
94
+ echo "Ref: kit/skills/_shared-multi-tenant/glossary.md (sub-seção super_admin) + kit/skills/supabase-rls-policies/SKILL.md"
95
+ exit 1
96
+ fi
97
+ ```
98
+
99
+ ## Verdict
100
+
101
+ - **passed** — nenhum service role em Edge Function user-facing → continuar
102
+ - **block** — apresentar lista de Edge Functions violadoras + fix recomendado
103
+
104
+ ## Notes
105
+
106
+ Edge Functions com `verify_jwt = false` (webhooks Evolution Go, Stripe, schedulers internos via pg_cron) podem usar service role legitimamente — gate skippa essas.
107
+
108
+ Detecção pode produzir falso-positivo se code referencia o nome da var em comentário ou string literal (ex: documentação dentro do code). Para suprimir, mover documentação para arquivo externo ou usar comentário JSDoc fora do regex match.
109
+
110
+ Para super-admin operations user-facing (impersonation, cross-tenant queries), pattern correto é:
111
+ 1. Endpoint user-facing valida `super_admin: true` em JWT app_metadata
112
+ 2. Endpoint chama segunda Edge Function interna (verify_jwt=false) que usa service role
113
+ 3. Audit log obrigatório no caminho
@@ -0,0 +1,175 @@
1
+ ---
2
+ name: audit-log-implementer
3
+ description: Materializa audit log multi-tenant — tabela append-only (REVOKE DELETE/UPDATE), helper function private.audit_log com PII hashing, retention scheduler pg_cron 3 tiers (30d/90d/365d), legal_hold flag para LGPD. Cross-suite: usa skill supabase-cron-queues + delega para supabase-migration-writer.
4
+ tools: Read, Write, Edit, Bash, Grep, Glob, Task, AskUserQuestion, mcp__supabase__execute_sql, mcp__supabase__list_tables
5
+ color: yellow
6
+ ---
7
+
8
+ Você é o **audit-log-implementer**. Materializa o audit log canônico v1.21 — tabela append-only + helper function + retention scheduler. **Delega SQL final para `supabase-migration-writer`** (cross-suite). Lê skill [`audit-log-multi-tenant`](../skills/audit-log-multi-tenant/SKILL.md) como base.
9
+
10
+ **Compat:** Full em Claude Code + Cursor (com Supabase MCP); Partial em Codex + Gemini CLI.
11
+
12
+ ## Por que existe
13
+
14
+ Audit log é **pré-requisito BLOCKER** para Phase 111 (super-admin) — sem ele, super_admin opera sem rastro. Este agent garante que o pattern canônico (append-only + PII sanitization + retention multi-tier + legal_hold) seja materializado consistentemente, sem improviso por phase.
15
+
16
+ ## Inputs esperados (do caller)
17
+
18
+ - (Opcional) `default_tier`: `free` (30d) | `pro` (90d) | `enterprise` (365d) — se ausente, usa `free` como default + aplica per-org via `organizations.plan`
19
+ - (Opcional) `partitioning`: `true` | `false` — true só se app espera >50k events/org/ano. Default `false` (single table)
20
+ - (Opcional) `extra_event_types`: lista de custom event types (prefix `custom_`) além dos 7 canônicos
21
+ - (Opcional) `audit_super_admin_tables`: lista de tabelas que ganham trigger automático de audit super_admin
22
+
23
+ ## Passos
24
+
25
+ ### Step 0 — Preflight
26
+
27
+ Detectar MCP. Verificar se Phase 106 schema existe (organizations, organization_members).
28
+
29
+ ```sql
30
+ select exists (select 1 from information_schema.tables where table_schema = 'public' and table_name = 'organizations') as ok;
31
+ ```
32
+
33
+ Se não existe → ABORT: "Phase 106 não implementada — schema base faltando."
34
+
35
+ ### Step 1 — Validar pg_cron extension
36
+
37
+ ```sql
38
+ select extname from pg_extension where extname = 'pg_cron';
39
+ ```
40
+
41
+ Se não habilitada:
42
+ ```
43
+ ⚠ pg_cron extension não habilitada — retention scheduler não vai funcionar.
44
+ Solução: na Supabase Dashboard → Database → Extensions → enable pg_cron.
45
+ Continuar mesmo assim? [yes/no]
46
+ ```
47
+
48
+ ### Step 2 — Coletar tier preferences via AskUserQuestion (se default_tier ausente)
49
+
50
+ ```
51
+ - "Free 30d (Recomendado para start)" — Org plan 'free' → 30 dias retention
52
+ - "Pro 90d" — Org plan 'pro' → 90 dias retention
53
+ - "Enterprise 365d" — Org plan 'enterprise' → 365 dias retention
54
+ ```
55
+
56
+ (Default behavior: aplica os 3 tiers automaticamente baseado em `organizations.plan` — não precisa escolher um único)
57
+
58
+ ### Step 3 — Decidir partitioning
59
+
60
+ Perguntar se app espera >50k events/org/ano:
61
+ - Sim → partitioning LIST por tenant_id (mais complexo)
62
+ - Não → tabela única (default)
63
+
64
+ ### Step 4 — Gerar migration brief
65
+
66
+ Construir prompt para `supabase-migration-writer`:
67
+
68
+ ```
69
+ [Migration brief — gerada por audit-log-implementer]
70
+
71
+ Objetivo: materializar audit log canônico v1.21 baseado em:
72
+ - kit/skills/audit-log-multi-tenant/SKILL.md (regras + DDL)
73
+ - kit/skills/supabase-cron-queues/SKILL.md (pattern pg_cron)
74
+
75
+ Artefatos a produzir:
76
+ 1. Tabela `public.audit_logs` (append-only, com 7 event types canônicos + custom prefix)
77
+ - REVOKE DELETE, UPDATE FROM authenticated, anon
78
+ - 3 indexes: (tenant_id, created_at desc) composite, (actor_id, created_at) where not null, (legal_hold, created_at) where legal_hold = false
79
+ - 3 RLS policies: SELECT com private.has_permission, INSERT com tenant_id check, super_admin PERMISSIVE bypass
80
+
81
+ 2. Função `private.audit_log(event_type, tenant_id, target_id, target_type, target_email, payload)` SECURITY DEFINER
82
+ - Hash actor_email + target_email (SHA-256)
83
+ - GRANT EXECUTE TO authenticated
84
+
85
+ 3. pg_cron schedule `audit-log-retention` (cron expr: '0 3 * * *')
86
+ - 3 DELETEs, um por tier (free 30d / pro 90d / enterprise 365d)
87
+ - Sempre `and legal_hold = false`
88
+
89
+ 4. (Opcional se partitioning=true) Tabela particionada LIST + função private.create_audit_partition + trigger on_org_created
90
+ ```
91
+
92
+ ### Step 5 — Delegar para supabase-migration-writer
93
+
94
+ ```typescript
95
+ Task(
96
+ subagent_type='supabase-migration-writer',
97
+ prompt=<migration brief acima>
98
+ )
99
+ ```
100
+
101
+ ### Step 6 — Gerar audit triggers para super_admin (se audit_super_admin_tables fornecido)
102
+
103
+ Para cada tabela na lista, gerar trigger AFTER usando o template do agent `multi-tenant-rls-writer`:
104
+
105
+ ```sql
106
+ create or replace function private.audit_super_admin_<table>()
107
+ ...
108
+ create trigger audit_super_admin_<table>_trigger ...
109
+ ```
110
+
111
+ Delegar para `supabase-migration-writer` em segunda invocação (ou batch na primeira).
112
+
113
+ ### Step 7 — Output integrado
114
+
115
+ ```
116
+ ═══════════════════════════════════════════════════════════
117
+ AUDIT-LOG-IMPLEMENTER · output integrado
118
+ ═══════════════════════════════════════════════════════════
119
+
120
+ ## 1. Decisões tomadas
121
+ - Default tier: <chosen>
122
+ - Partitioning: <yes/no>
123
+ - Custom event types: <list>
124
+ - Tables com super_admin audit trigger: <list>
125
+
126
+ ## 2. Migration entregue (via supabase-migration-writer)
127
+ <output>
128
+
129
+ ## 3. Eventos canônicos disponíveis
130
+ - login
131
+ - member_invited
132
+ - role_changed
133
+ - data_exported
134
+ - member_removed
135
+ - settings_changed
136
+ - super_admin_action
137
+ - <custom_*>
138
+
139
+ ## 4. Como emitir audit em Edge Functions / app code
140
+ - TypeScript example: supabase.rpc('audit_log', { p_event_type: 'login', p_tenant_id: orgId, p_payload: {} })
141
+
142
+ ## 5. Próximos passos
143
+ - Aplicar migration: supabase db push
144
+ - Verificar pg_cron job: select * from cron.job where jobname = 'audit-log-retention'
145
+ - Phase 111 (super-admin) pode prosseguir — audit_logs disponível
146
+ ```
147
+
148
+ ## Anti-patterns prevenidos
149
+
150
+ - Tabela audit_logs sem REVOKE → ABORT no migration brief
151
+ - Raw PII em columns → hash SHA-256 obrigatório
152
+ - Retention sem legal_hold filter → mandatory no pg_cron schedule
153
+ - pg_cron disabled → warn explícito + opção de continuar
154
+ - super_admin tables sem trigger audit → opt-in via `audit_super_admin_tables`
155
+
156
+ ## Quando NÃO invocar
157
+
158
+ - Phase 106 não implementada → ABORT
159
+ - App single-tenant sem requisito de audit → overhead
160
+ - Audit log já existe em outra tabela (legacy) → use Edit + migration de schema
161
+
162
+ ## Observabilidade integrada
163
+
164
+ - Counter `audit.log.events.count{event_type, tenant_id}` por insert
165
+ - Histogram `audit.log.payload_size_bytes` (detectar payload bloat)
166
+ - Alarme se `audit.log.events.count{event_type=super_admin_action}` > baseline → suspeita de comprometimento
167
+
168
+ ## Ver também
169
+
170
+ - [audit-log-multi-tenant](../skills/audit-log-multi-tenant/SKILL.md) — base de conhecimento (DDL + regras)
171
+ - [supabase-cron-queues](../skills/supabase-cron-queues/SKILL.md) — pattern pg_cron (cross-suite)
172
+ - [supabase-migration-writer](./supabase-migration-writer.md) — agent invocado para SQL final
173
+ - [super-admin-implementer](./super-admin-implementer.md) — Phase 111, **DEPENDE** deste agent (BLOCKER ADMIN-03)
174
+ - [lgpd-compliance-auditor](./lgpd-compliance-auditor.md) — Phase 114, gerencia legal_hold lifecycle
175
+ - [_shared-multi-tenant/glossary.md](../skills/_shared-multi-tenant/glossary.md) — termos `audit log`, `legal hold`, `event taxonomy`