@luanpdd/kit-mcp 1.7.0 → 1.9.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 (70) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +39 -1
  3. package/gates/agent-no-recursive-dispatch.md +48 -0
  4. package/gates/budget-description.md +68 -0
  5. package/gates/no-personal-uuid.md +72 -0
  6. package/gates/obs-agents-mcp-supabase.md +86 -0
  7. package/gates/obs-skills-frontmatter.md +76 -0
  8. package/gates/omm-no-regression.md +83 -0
  9. package/gates/skill-must-include.md +71 -0
  10. package/gates/sync-idempotent.md +62 -0
  11. package/kit/agents/burn-rate-forecaster.md +160 -0
  12. package/kit/agents/codebase-mapper.md +1 -1
  13. package/kit/agents/executor.md +17 -0
  14. package/kit/agents/incident-investigator.md +245 -0
  15. package/kit/agents/observability-instrumenter.md +200 -0
  16. package/kit/agents/omm-auditor.md +199 -0
  17. package/kit/agents/planner.md +35 -0
  18. package/kit/agents/project-researcher.md +1 -1
  19. package/kit/agents/schema-checker.md +4 -4
  20. package/kit/agents/slo-engineer.md +224 -0
  21. package/kit/agents/supabase-architect.md +166 -0
  22. package/kit/agents/supabase-auth-bootstrapper.md +315 -0
  23. package/kit/agents/supabase-edge-fn-writer.md +207 -0
  24. package/kit/agents/supabase-migration-writer.md +174 -0
  25. package/kit/agents/supabase-realtime-implementer.md +275 -0
  26. package/kit/agents/supabase-rls-writer.md +235 -0
  27. package/kit/agents/supabase-storage-implementer.md +258 -0
  28. package/kit/agents/user-profiler.md +1 -1
  29. package/kit/agents/verifier.md +1 -1
  30. package/kit/commands/auditar-marco.md +22 -1
  31. package/kit/commands/auditar-observabilidade.md +103 -0
  32. package/kit/commands/burn-rate-status.md +140 -0
  33. package/kit/commands/concluir-marco.md +19 -1
  34. package/kit/commands/definir-slo.md +108 -0
  35. package/kit/commands/depurar.md +17 -0
  36. package/kit/commands/discutir-fase.md +26 -0
  37. package/kit/commands/fazer.md +15 -0
  38. package/kit/commands/forense.md +20 -1
  39. package/kit/commands/instrumentar-fase.md +200 -0
  40. package/kit/commands/investigar-producao.md +162 -0
  41. package/kit/commands/observabilidade.md +116 -0
  42. package/kit/commands/planejar-fase.md +20 -0
  43. package/kit/commands/supabase.md +148 -0
  44. package/kit/commands/verificar-trabalho.md +26 -0
  45. package/kit/framework/workflows/discuss-phase.md +19 -0
  46. package/kit/framework/workflows/plan-phase.md +25 -0
  47. package/kit/skills/_shared-observability/glossary.md +396 -0
  48. package/kit/skills/_shared-supabase/glossary.md +180 -0
  49. package/kit/skills/burn-rate-alerting/SKILL.md +258 -0
  50. package/kit/skills/core-analysis-loop/SKILL.md +352 -0
  51. package/kit/skills/distributed-tracing/SKILL.md +362 -0
  52. package/kit/skills/event-based-slos/SKILL.md +274 -0
  53. package/kit/skills/observability-driven-development/SKILL.md +315 -0
  54. package/kit/skills/observability-maturity-model/SKILL.md +222 -0
  55. package/kit/skills/opentelemetry-standard/SKILL.md +351 -0
  56. package/kit/skills/structured-events/SKILL.md +265 -0
  57. package/kit/skills/supabase-auth-ssr/SKILL.md +260 -0
  58. package/kit/skills/supabase-cron-queues/SKILL.md +266 -0
  59. package/kit/skills/supabase-database-functions/SKILL.md +247 -0
  60. package/kit/skills/supabase-declarative-schema/SKILL.md +183 -0
  61. package/kit/skills/supabase-edge-functions/SKILL.md +242 -0
  62. package/kit/skills/supabase-migrations/SKILL.md +175 -0
  63. package/kit/skills/supabase-pgvector-rag/SKILL.md +253 -0
  64. package/kit/skills/supabase-postgres-style/SKILL.md +138 -0
  65. package/kit/skills/supabase-realtime/SKILL.md +236 -0
  66. package/kit/skills/supabase-rls-policies/SKILL.md +185 -0
  67. package/kit/skills/supabase-storage/SKILL.md +234 -0
  68. package/kit/skills/telemetry-pipelines/SKILL.md +259 -0
  69. package/kit/skills/telemetry-sampling/SKILL.md +256 -0
  70. package/package.json +1 -1
@@ -0,0 +1,175 @@
1
+ ---
2
+ name: supabase-migrations
3
+ description: Use ao criar arquivos de migration Supabase — naming YYYYMMDDHHmmss_short.sql, header de metadados, RLS obrigatório em toda nova tabela, granular policies.
4
+ ---
5
+
6
+ # Supabase — Migrations
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando criar/editar arquivos em `supabase/migrations/`. Trigger phrases:
11
+
12
+ - "criar migration Supabase", "supabase migration new"
13
+ - "alterar schema do banco", "alter table"
14
+ - "criar nova tabela em Postgres/Supabase"
15
+ - "adicionar coluna a tabela existente"
16
+ - "drop column / drop table" (operações destrutivas — exige cuidado extra)
17
+
18
+ ## Regras absolutas
19
+
20
+ - **Naming canônico:** `YYYYMMDDHHmmss_short_description.sql` em UTC (ex: `20260506120000_create_tasks.sql`). Use `supabase migration new <name>` para gerar timestamp correto.
21
+ - **Header de metadados** no topo de cada migration (block comment) descrevendo Migration / Created / Purpose / Affects.
22
+ - **lowercase em todo SQL** (alinhado com `supabase-postgres-style`).
23
+ - **Comentários copiosos** em comandos destrutivos: `drop table`, `drop column`, `alter table ... drop column`, `truncate`, `delete from` em massa. Comentário explica o porquê + impacto.
24
+ - **`RLS` obrigatório em toda nova tabela** — `alter table public.<name> enable row level security;` no mesmo arquivo da criação.
25
+ - **`granular policies`** — uma `for select`, uma `for insert`, uma `for update`, uma `for delete`. **Nunca** `for all`.
26
+ - **`(select auth.uid())`** sempre wrapped (REGRA #1 de RLS).
27
+ - **Index nas colunas referenciadas por RLS:** `create index on public.<table> (user_id);` no mesmo arquivo.
28
+ - Idempotência onde possível: `create table if not exists`, `create index if not exists`. Migrations rodam em ordem mas tooling pode re-executar.
29
+ - Migrations são **append-only**. Para reverter, criar nova migration que desfaz — nunca editar migration já aplicada.
30
+
31
+ ## Patterns canônicos
32
+
33
+ ### Criar tabela com RLS + policies granulares + index
34
+
35
+ ```sql
36
+ /*
37
+ Migration: create_tasks
38
+ Created: 2026-05-06
39
+ Purpose: Cria tabela tasks com RLS habilitado e policies granulares por operação.
40
+ Affects: public.tasks (new), public.tasks policies (new — 4 policies)
41
+ */
42
+
43
+ create table if not exists public.tasks (
44
+ id uuid primary key default gen_random_uuid(),
45
+ user_id uuid not null references auth.users (id) on delete cascade,
46
+ title text not null,
47
+ status text not null default 'todo',
48
+ created_at timestamptz not null default now(),
49
+ updated_at timestamptz not null default now()
50
+ );
51
+
52
+ alter table public.tasks enable row level security;
53
+
54
+ -- granular policies: uma por operação por role
55
+ create policy "users_select_own_tasks"
56
+ on public.tasks for select to authenticated
57
+ using ((select auth.uid()) = user_id);
58
+
59
+ create policy "users_insert_own_tasks"
60
+ on public.tasks for insert to authenticated
61
+ with check ((select auth.uid()) = user_id);
62
+
63
+ create policy "users_update_own_tasks"
64
+ on public.tasks for update to authenticated
65
+ using ((select auth.uid()) = user_id)
66
+ with check ((select auth.uid()) = user_id);
67
+
68
+ create policy "users_delete_own_tasks"
69
+ on public.tasks for delete to authenticated
70
+ using ((select auth.uid()) = user_id);
71
+
72
+ -- index obrigatório nas colunas usadas pela policy
73
+ create index if not exists tasks_user_id_idx on public.tasks (user_id);
74
+ ```
75
+
76
+ ### Adicionar coluna a tabela existente
77
+
78
+ ```sql
79
+ /*
80
+ Migration: add_priority_to_tasks
81
+ Created: 2026-05-06
82
+ Purpose: Adiciona coluna priority (low/medium/high) a tasks com default low.
83
+ Affects: public.tasks (column added — non-destructive)
84
+ */
85
+
86
+ alter table public.tasks
87
+ add column if not exists priority text not null default 'low';
88
+
89
+ -- check constraint para enum-like
90
+ alter table public.tasks
91
+ add constraint tasks_priority_check
92
+ check (priority in ('low', 'medium', 'high'));
93
+ ```
94
+
95
+ ### Operação destrutiva — drop column com comentário extensivo
96
+
97
+ ```sql
98
+ /*
99
+ Migration: drop_legacy_subtitle_column
100
+ Created: 2026-05-06
101
+ Purpose: Remove coluna subtitle (deprecated em v3.0 — nunca foi usada em produção).
102
+ Affects: public.tasks (column dropped — DESTRUCTIVE)
103
+ Risk: Baixo — coluna nullable nunca populada (validado via select count(*) where subtitle is not null = 0).
104
+ Rollback: criar nova migration `add subtitle column` se necessário.
105
+ */
106
+
107
+ -- DROP de coluna deprecated. Validado upstream: zero linhas com valor não-null.
108
+ -- Operação destrutiva — irreversível sem backup.
109
+ alter table public.tasks
110
+ drop column if exists subtitle;
111
+ ```
112
+
113
+ ## Anti-patterns
114
+
115
+ ### Anti-pattern 1: Criar tabela sem RLS
116
+
117
+ **Errado:**
118
+ ```sql
119
+ create table public.tasks (
120
+ id uuid primary key default gen_random_uuid(),
121
+ user_id uuid not null,
122
+ title text not null
123
+ );
124
+ -- esqueceu enable row level security
125
+ ```
126
+
127
+ **Por quê:** sem RLS, tabela exposta ao role `anon` e `authenticated` sem filtro — qualquer cliente lê tudo. RLS habilitado sem policies bloqueia tudo (mais seguro como default que deixar aberto).
128
+
129
+ **Certo:** sempre `alter table public.tasks enable row level security;` + policies granulares no mesmo arquivo.
130
+
131
+ ### Anti-pattern 2: `for all` em vez de granular policies
132
+
133
+ **Errado:**
134
+ ```sql
135
+ create policy "users_manage_tasks" on public.tasks
136
+ for all to authenticated
137
+ using ((select auth.uid()) = user_id);
138
+ ```
139
+
140
+ **Por quê:** mistura `using` (controla SELECT/UPDATE/DELETE) com `with check` (controla INSERT/UPDATE) — em UPDATE você pode querer regras diferentes para "qual linha tocar" vs "qual estado novo".
141
+
142
+ **Certo:** 4 policies separadas (uma por operação) — ver pattern "Criar tabela" acima.
143
+
144
+ ### Anti-pattern 3: `drop column` sem comentário
145
+
146
+ **Errado:**
147
+ ```sql
148
+ alter table public.tasks drop column legacy_field;
149
+ ```
150
+
151
+ **Por quê:** futuros leitores não sabem por que a coluna foi removida; rollback fica difícil; risk não documentado.
152
+
153
+ **Certo:** comentário no header explica Purpose + Affects + Risk + Rollback (ver pattern destrutivo acima).
154
+
155
+ ### Anti-pattern 4: `auth.uid()` sem `(select)` wrapper
156
+
157
+ **Errado:**
158
+ ```sql
159
+ using (auth.uid() = user_id)
160
+ ```
161
+
162
+ **Por quê:** degradação 1000× em queries com filtro RLS (Postgres reavalia por linha).
163
+
164
+ **Certo:**
165
+ ```sql
166
+ using ((select auth.uid()) = user_id)
167
+ ```
168
+
169
+ ## Ver também
170
+
171
+ - [supabase-postgres-style](../supabase-postgres-style/SKILL.md) — convenção de naming + style aplicada
172
+ - [supabase-rls-policies](../supabase-rls-policies/SKILL.md) — granular policies + WARNING user_metadata
173
+ - [supabase-database-functions](../supabase-database-functions/SKILL.md) — funções com `set search_path = ''`
174
+ - [supabase-declarative-schema](../supabase-declarative-schema/SKILL.md) — workflow alternativo (declarative-first → diff)
175
+ - [glossário](../_shared-supabase/glossary.md) — termos PT-BR↔EN + comandos CLI
@@ -0,0 +1,253 @@
1
+ ---
2
+ name: supabase-pgvector-rag
3
+ description: Use ao implementar RAG com pgvector — create extension vector, dim consistente, HNSW vs IVFFlat, operadores <=>/<#>, RAG with permissions via RLS, chunking.
4
+ ---
5
+
6
+ # Supabase — pgvector + RAG
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando implementar embeddings, similarity search ou RAG (Retrieval-Augmented Generation) com Supabase. Trigger phrases:
11
+
12
+ - "pgvector", "vector embeddings"
13
+ - "RAG Supabase", "retrieval augmented generation"
14
+ - "semantic search Postgres"
15
+ - "HNSW vs IVFFlat"
16
+ - "embedding dimension"
17
+ - "match_documents function"
18
+
19
+ ## Regras absolutas
20
+
21
+ - **Setup:** `create extension if not exists vector;` em migration ou `supabase/schemas/`.
22
+ - **Dimension fixa por modelo** — defina `embedding vector(N)` com N = dim do modelo: 1536 (OpenAI ada-002), 768 (nomic-embed-text), 384 (all-MiniLM-L6-v2). Mismatch = silent fail ou matches aleatórios.
23
+ - **Index obrigatório** — sem index, similarity search faz full scan e degrada drasticamente em > 10k linhas.
24
+ - **`HNSW`** = default em 2026 — recall melhor, queries mais rápidas com mais data. Build mais lento.
25
+ - **`IVFFlat`** = alternativa quando build time domina (datasets dinâmicos com re-build frequente). Recall menor.
26
+ - **Distance operators canônicos:**
27
+ - **`<=>`** — cosine distance (mais comum em embeddings normalizados)
28
+ - **`<#>`** — negative inner product
29
+ - **`<->`** — L2 (euclidean) distance
30
+ - **`RAG with permissions`** — combine similarity search com RLS na tabela source. Sem isso, retrieval vaza documents entre tenants.
31
+ - **Chunking:** 200-500 tokens com overlap 10-20%. Chunks > 1k tokens degradam embeddings; < 100 perdem contexto.
32
+ - **Embedding generation server-side** — geração via Edge Function ou worker (model API key não vai para client).
33
+
34
+ ## Patterns canônicos
35
+
36
+ ### Schema com RLS + HNSW index
37
+
38
+ ```sql
39
+ -- PT-BR: extension uma vez por projeto
40
+ create extension if not exists vector;
41
+
42
+ -- PT-BR: documents com embedding e user_id para RAG with permissions
43
+ create table public.documents (
44
+ id uuid primary key default gen_random_uuid(),
45
+ user_id uuid not null references auth.users (id) on delete cascade,
46
+ content text not null, -- PT-BR: chunk de texto (200-500 tokens)
47
+ embedding vector(1536) not null, -- PT-BR: dim casa com modelo
48
+ metadata jsonb default '{}'::jsonb,
49
+ created_at timestamptz default now()
50
+ );
51
+
52
+ -- PT-BR: HNSW com cosine distance (default 2026)
53
+ create index documents_embedding_hnsw_idx
54
+ on public.documents
55
+ using hnsw (embedding vector_cosine_ops);
56
+
57
+ -- PT-BR: RLS — RAG with permissions
58
+ alter table public.documents enable row level security;
59
+
60
+ create policy "users_read_own_docs"
61
+ on public.documents for select to authenticated
62
+ using ((select auth.uid()) = user_id);
63
+
64
+ create policy "users_insert_own_docs"
65
+ on public.documents for insert to authenticated
66
+ with check ((select auth.uid()) = user_id);
67
+
68
+ create index documents_user_id_idx on public.documents (user_id);
69
+ ```
70
+
71
+ ### Função `match_documents` — RAG with permissions
72
+
73
+ ```sql
74
+ create or replace function public.match_documents(
75
+ query_embedding vector(1536),
76
+ match_threshold float,
77
+ match_count int
78
+ )
79
+ returns table (
80
+ id uuid,
81
+ content text,
82
+ similarity float,
83
+ metadata jsonb
84
+ )
85
+ language plpgsql
86
+ security invoker -- PT-BR: invoker → respeita RLS do caller
87
+ set search_path = ''
88
+ stable
89
+ as $$
90
+ begin
91
+ return query
92
+ select
93
+ d.id,
94
+ d.content,
95
+ 1 - (d.embedding <=> query_embedding) as similarity,
96
+ d.metadata
97
+ from public.documents as d
98
+ where 1 - (d.embedding <=> query_embedding) > match_threshold
99
+ order by d.embedding <=> query_embedding
100
+ limit match_count;
101
+ end;
102
+ $$;
103
+ ```
104
+
105
+ **Por que `security invoker`:** garante que a função só retorna documentos que o caller (usuário autenticado) tem permissão de ver via RLS. Sem RLS, qualquer caller via similarity recupera documents de outros tenants.
106
+
107
+ ### Uso da função do client
108
+
109
+ ```ts
110
+ // PT-BR: gerar embedding (em Edge Function, server-side) e chamar match_documents
111
+ const queryEmbedding = await embedQuery(userQuestion) // dim 1536
112
+
113
+ const { data: matches } = await supabase.rpc('match_documents', {
114
+ query_embedding: queryEmbedding,
115
+ match_threshold: 0.78,
116
+ match_count: 10,
117
+ })
118
+
119
+ // matches: [{ id, content, similarity, metadata }, ...] já filtrado por RLS
120
+ const context = matches.map((m) => m.content).join('\n\n')
121
+ const answer = await llmComplete({ context, question: userQuestion })
122
+ ```
123
+
124
+ ### IVFFlat alternativa
125
+
126
+ ```sql
127
+ -- PT-BR: usar IVFFlat quando build time importa (dataset dinâmico)
128
+ -- lists = sqrt(N) é heurística para N total de linhas
129
+ create index documents_embedding_ivfflat_idx
130
+ on public.documents
131
+ using ivfflat (embedding vector_cosine_ops)
132
+ with (lists = 100); -- ajustar conforme volume
133
+ ```
134
+
135
+ ### Geração de embeddings em Edge Function
136
+
137
+ ```ts
138
+ // supabase/functions/embed-document/index.ts
139
+ // PT-BR: chunking + embedding + insert
140
+ import { createClient } from 'npm:@supabase/supabase-js@2'
141
+ import OpenAI from 'npm:openai@4'
142
+
143
+ Deno.serve(async (req) => {
144
+ const { user_id, document } = await req.json()
145
+ const openai = new OpenAI({ apiKey: Deno.env.get('OPENAI_API_KEY') })
146
+ const supabase = createClient(
147
+ Deno.env.get('SUPABASE_URL')!,
148
+ Deno.env.get('SUPABASE_SECRET_KEYS')!
149
+ )
150
+
151
+ // PT-BR: chunk em pedaços de ~400 tokens com overlap 20%
152
+ const chunks = chunkText(document, { size: 400, overlap: 80 })
153
+
154
+ for (const chunk of chunks) {
155
+ const embedRes = await openai.embeddings.create({
156
+ model: 'text-embedding-3-small', // dim 1536
157
+ input: chunk,
158
+ })
159
+ await supabase.from('documents').insert({
160
+ user_id,
161
+ content: chunk,
162
+ embedding: embedRes.data[0].embedding,
163
+ })
164
+ }
165
+
166
+ return new Response('embedded')
167
+ })
168
+ ```
169
+
170
+ ## Anti-patterns
171
+
172
+ ### Anti-pattern 1: Dim mismatch entre modelo e coluna
173
+
174
+ **Errado:**
175
+ ```sql
176
+ create table documents (embedding vector(1536) not null);
177
+ ```
178
+ ```ts
179
+ // PT-BR: usa nomic-embed-text que retorna dim 768
180
+ const embedding = await nomicEmbed(text) // 768
181
+ await supabase.from('documents').insert({ embedding }) // ⚠ insert falha
182
+ ```
183
+
184
+ **Por quê:** Postgres rejeita insert com dim mismatch (`expected 1536 dimensions, got 768`). Em pior caso, se aceito, similarity retorna ranking aleatório.
185
+
186
+ **Certo:** alinhe dim:
187
+ ```sql
188
+ create table documents (embedding vector(768) not null);
189
+ ```
190
+ ou troque o modelo para um que retorne 1536.
191
+
192
+ ### Anti-pattern 2: Similarity search sem RLS — vazamento RAG
193
+
194
+ **Errado:**
195
+ ```sql
196
+ -- PT-BR: tabela documents sem RLS
197
+ create table public.documents (
198
+ id uuid primary key,
199
+ content text,
200
+ embedding vector(1536)
201
+ );
202
+ ```
203
+
204
+ **Por quê:** qualquer authenticated chama `match_documents` e recupera documents de outros usuários. Em apps multi-tenant, é vazamento crítico — RAG do tenant A vê docs do tenant B.
205
+
206
+ **Certo:** habilite RLS + policies por `user_id`/`org_id` + use `security invoker` em funções de match (ver pattern canônico).
207
+
208
+ ### Anti-pattern 3: Chunks gigantes (> 1k tokens)
209
+
210
+ **Errado:**
211
+ ```ts
212
+ // PT-BR: chunk inteiro de documento (5k tokens)
213
+ const chunks = [fullDocument]
214
+ const embedding = await embed(fullDocument)
215
+ ```
216
+
217
+ **Por quê:** embeddings perdem detalhe semântico em chunks muito grandes. O modelo média muitos conceitos em um único vetor, similarity vira ruidosa. Ranking RAG fica ruim.
218
+
219
+ **Certo:** chunks de 200-500 tokens com overlap:
220
+ ```ts
221
+ const chunks = chunkText(fullDocument, { size: 400, overlap: 80 })
222
+ for (const chunk of chunks) {
223
+ await embed(chunk).then(insert)
224
+ }
225
+ ```
226
+
227
+ ### Anti-pattern 4: Sem index em coluna `embedding`
228
+
229
+ **Errado:**
230
+ ```sql
231
+ create table documents (embedding vector(1536) not null);
232
+ -- (esqueceu create index)
233
+ ```
234
+
235
+ **Por quê:** sem index, similarity search vira sequential scan. Em > 10k linhas, queries levam segundos a minutos. Em produção, app fica inviável.
236
+
237
+ **Certo:**
238
+ ```sql
239
+ create index documents_embedding_hnsw_idx on documents using hnsw (embedding vector_cosine_ops);
240
+ ```
241
+
242
+ ## Notas de futuro
243
+
244
+ - **Hybrid search** (FTS + vector com RRF — Reciprocal Rank Fusion) está coberto em skill `supabase-fts` (defer v1.9 — full-text search standalone).
245
+ - **Vector Buckets** e **Analytics Buckets** ainda em alpha em 2026 — mencione como existência mas não detalhe (pattern canônico evoluindo).
246
+
247
+ ## Ver também
248
+
249
+ - [supabase-rls-policies](../supabase-rls-policies/SKILL.md) — RLS é base de RAG with permissions
250
+ - [supabase-database-functions](../supabase-database-functions/SKILL.md) — função `match_documents` com `security invoker` + `set search_path = ''`
251
+ - [supabase-edge-functions](../supabase-edge-functions/SKILL.md) — geração de embeddings server-side
252
+ - [supabase-migrations](../supabase-migrations/SKILL.md) — schema com `vector` extension
253
+ - [glossário](../_shared-supabase/glossary.md) — operadores `<=>`/`<#>`/`<->`
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: supabase-postgres-style
3
+ description: Use ao escrever SQL para Postgres/Supabase — snake_case, lowercase reserved, plurais para tabelas e singular para colunas, ISO 8601, CTEs lineares.
4
+ ---
5
+
6
+ # Supabase — Postgres Style Guide
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando trabalhar com SQL em projeto Supabase/Postgres — definir schemas, escrever queries, criar tabelas/colunas, padronizar dates, decidir nomes. Trigger phrases:
11
+
12
+ - "criar tabela em postgres", "create table"
13
+ - "escrever query SQL para Supabase"
14
+ - "estilo de schema", "convenção de nomes em SQL"
15
+ - "estrutura de query complexa" (CTE vs subquery)
16
+
17
+ ## Regras absolutas
18
+
19
+ - **Sempre** use **`lowercase reserved`** words: `select`, `from`, `where`, `join`, `with`, `as`. **Nunca** `SELECT`, `FROM`, `WHERE` em maiúscula.
20
+ - **Sempre** use **`snake_case`** para tabelas, colunas, funções, índices. **Nunca** `camelCase` ou `PascalCase`.
21
+ - **Tabelas em plural** (`books`, `authors`, `users`); **colunas em singular** (`title`, `author_id`, `created_at`).
22
+ - **Datas em `ISO 8601`** com timezone: `timestamptz` (não `timestamp` sem tz). String literal: `'2026-05-06T12:00:00Z'`.
23
+ - Aliases descritivos com `as` **explícito**: `select b.title as book_title from books as b`. Nunca alias implícito.
24
+ - Evite `id` ambíguo. Em FKs use `<entity>_id` (`author_id`, `user_id`). Em PKs use `id` apenas se a tabela já é singular contextualmente.
25
+ - Para queries complexas: prefira **múltiplas CTEs lineares** sobre subqueries aninhadas. Cada CTE com 1 propósito + comentário.
26
+ - JOINs sempre com nomes completos da tabela qualificadora: `books.author_id = authors.id` (não aliases curtos como `b.x = a.y` sem `as`).
27
+
28
+ ## Patterns canônicos
29
+
30
+ ### Tabela típica
31
+
32
+ ```sql
33
+ -- estilo: lowercase reserved + snake_case + tabela em plural + colunas em singular
34
+ create table public.books (
35
+ id uuid primary key default gen_random_uuid(),
36
+ title text not null,
37
+ author_id uuid references public.authors (id) on delete cascade,
38
+ published_at timestamptz, -- ISO 8601 com timezone
39
+ created_at timestamptz not null default now(),
40
+ updated_at timestamptz not null default now()
41
+ );
42
+
43
+ -- comentário descritivo na tabela (até 1024 chars)
44
+ comment on table public.books is 'Catálogo de livros disponíveis na biblioteca.';
45
+ ```
46
+
47
+ ### Query simples (uma linha por cláusula)
48
+
49
+ ```sql
50
+ -- query curta: pode ficar em poucas linhas
51
+ select id, title, author_id
52
+ from public.books
53
+ where published_at is not null
54
+ order by published_at desc
55
+ limit 50;
56
+ ```
57
+
58
+ ### Query complexa com CTEs lineares
59
+
60
+ ```sql
61
+ -- preferir CTEs lineares — cada uma com 1 propósito
62
+ with recent_books as (
63
+ -- 1. livros publicados nos últimos 30 dias
64
+ select id, title, author_id, published_at
65
+ from public.books
66
+ where published_at >= now() - interval '30 days'
67
+ ),
68
+ author_stats as (
69
+ -- 2. agregação por autor sobre os livros recentes
70
+ select author_id, count(*) as total_recent
71
+ from recent_books
72
+ group by author_id
73
+ )
74
+ select a.name as author_name, s.total_recent
75
+ from author_stats as s
76
+ join public.authors as a on a.id = s.author_id
77
+ order by s.total_recent desc;
78
+ ```
79
+
80
+ ## Anti-patterns
81
+
82
+ ### Anti-pattern 1: Reserved words em maiúscula + mixed case
83
+
84
+ **Errado:**
85
+ ```sql
86
+ SELECT * FROM Books WHERE Title='X'
87
+ ```
88
+
89
+ **Por quê:** vai contra convenção da comunidade Postgres + dificulta diff em pull requests. Identificadores `Books` exigirão quoting (`"Books"`) sempre, ou o Postgres dobra para `books` quietly.
90
+
91
+ **Certo:**
92
+ ```sql
93
+ select * from books where title = 'X'
94
+ ```
95
+
96
+ ### Anti-pattern 2: `timestamp` sem timezone + camelCase
97
+
98
+ **Errado:**
99
+ ```sql
100
+ create table users (
101
+ id int primary key,
102
+ createdAt timestamp, -- sem timezone
103
+ fullName text -- camelCase
104
+ );
105
+ ```
106
+
107
+ **Por quê:** `timestamp` (sem `tz`) não preserva timezone — converte tudo para o timezone do servidor; ambíguo em apps multi-região. `camelCase` em SQL é estilizado por engine driver (caso por caso) e quebra em ferramentas que esperam snake_case.
108
+
109
+ **Certo:**
110
+ ```sql
111
+ create table users (
112
+ id uuid primary key default gen_random_uuid(),
113
+ created_at timestamptz not null default now(),
114
+ full_name text
115
+ );
116
+ ```
117
+
118
+ ### Anti-pattern 3: subqueries aninhadas em vez de CTEs
119
+
120
+ **Errado:**
121
+ ```sql
122
+ select * from (
123
+ select author_id, count(*) from (
124
+ select * from books where published_at > now() - interval '30 days'
125
+ ) recent group by author_id
126
+ ) ranked where count > 5;
127
+ ```
128
+
129
+ **Por quê:** ilegível, impossível de comentar cada nível, query plan harder to read.
130
+
131
+ **Certo:** ver "Query complexa com CTEs lineares" acima.
132
+
133
+ ## Ver também
134
+
135
+ - [supabase-migrations](../supabase-migrations/SKILL.md) — estilo aplicado em arquivos de migration
136
+ - [supabase-database-functions](../supabase-database-functions/SKILL.md) — estilo aplicado em funções Postgres
137
+ - [supabase-rls-policies](../supabase-rls-policies/SKILL.md) — convenção de naming em policies
138
+ - [glossário](../_shared-supabase/glossary.md) — termos PT-BR↔EN + comandos CLI canônicos