@luanpdd/kit-mcp 1.9.0 → 1.11.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 (84) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +58 -0
  3. package/gates/ai-prompt-stability.md +120 -0
  4. package/gates/golden-signals-coverage.md +133 -0
  5. package/gates/legacy-refactor-safety.md +178 -0
  6. package/gates/observability-coverage.md +151 -0
  7. package/gates/postmortem-template-required.md +127 -0
  8. package/gates/prr-checklist-coverage.md +128 -0
  9. package/gates/release-pipeline-policy.md +132 -0
  10. package/kit/COMANDOS.md +15 -0
  11. package/kit/agents/ai-mutation-tester.md +298 -0
  12. package/kit/agents/cascading-failures-auditor.md +306 -0
  13. package/kit/agents/executor.md +13 -0
  14. package/kit/agents/golden-signals-instrumenter.md +241 -0
  15. package/kit/agents/legacy-characterizer.md +378 -0
  16. package/kit/agents/load-shedding-instrumenter.md +297 -0
  17. package/kit/agents/observability-coverage-auditor.md +325 -0
  18. package/kit/agents/omm-auditor.md +99 -0
  19. package/kit/agents/payload-capture-instrumenter.md +283 -0
  20. package/kit/agents/planner.md +29 -0
  21. package/kit/agents/postmortem-writer.md +282 -0
  22. package/kit/agents/prr-conductor.md +296 -0
  23. package/kit/agents/refactor-safety-auditor.md +414 -0
  24. package/kit/agents/release-pipeline-auditor.md +360 -0
  25. package/kit/agents/seam-finder.md +367 -0
  26. package/kit/agents/shotgun-surgery-detector.md +359 -0
  27. package/kit/agents/storytelling-analyst.md +309 -0
  28. package/kit/agents/supabase-architect.md +49 -0
  29. package/kit/agents/supabase-edge-fn-writer.md +114 -0
  30. package/kit/agents/supabase-migration-writer.md +80 -0
  31. package/kit/agents/supabase-storage-implementer.md +156 -0
  32. package/kit/agents/toil-auditor.md +277 -0
  33. package/kit/agents/verifier.md +30 -0
  34. package/kit/commands/auditar-cascading.md +111 -0
  35. package/kit/commands/auditar-marco.md +124 -1
  36. package/kit/commands/auditar-observabilidade-cobertura.md +183 -0
  37. package/kit/commands/auditar-refactor.md +219 -0
  38. package/kit/commands/auditar-release.md +109 -0
  39. package/kit/commands/auditar-toil.md +129 -0
  40. package/kit/commands/capturar-payloads.md +193 -0
  41. package/kit/commands/caracterizar-prompt.md +195 -0
  42. package/kit/commands/caracterizar.md +212 -0
  43. package/kit/commands/concluir-marco.md +95 -1
  44. package/kit/commands/detectar-duplicacao.md +197 -0
  45. package/kit/commands/discutir-fase.md +41 -0
  46. package/kit/commands/encontrar-seams.md +136 -0
  47. package/kit/commands/forense.md +103 -1
  48. package/kit/commands/golden-signals.md +142 -0
  49. package/kit/commands/legacy.md +263 -0
  50. package/kit/commands/load-shedding.md +117 -0
  51. package/kit/commands/observabilidade.md +2 -0
  52. package/kit/commands/postmortem.md +179 -0
  53. package/kit/commands/prr.md +205 -0
  54. package/kit/commands/refactor-seguro.md +321 -0
  55. package/kit/commands/risk-budget.md +220 -0
  56. package/kit/commands/sre.md +230 -0
  57. package/kit/commands/storytelling.md +179 -0
  58. package/kit/skills/_shared-legacy/glossary.md +389 -0
  59. package/kit/skills/_shared-sre/glossary.md +712 -0
  60. package/kit/skills/ai-prompt-characterization/SKILL.md +335 -0
  61. package/kit/skills/blameless-postmortems/SKILL.md +340 -0
  62. package/kit/skills/cascading-failures/SKILL.md +307 -0
  63. package/kit/skills/eliminating-toil/SKILL.md +243 -0
  64. package/kit/skills/event-based-slos/SKILL.md +22 -0
  65. package/kit/skills/four-golden-signals/SKILL.md +314 -0
  66. package/kit/skills/hermetic-builds/SKILL.md +323 -0
  67. package/kit/skills/legacy-api-only-applications/SKILL.md +358 -0
  68. package/kit/skills/legacy-characterization-tests/SKILL.md +330 -0
  69. package/kit/skills/legacy-effect-analysis/SKILL.md +331 -0
  70. package/kit/skills/legacy-extract-class/SKILL.md +203 -0
  71. package/kit/skills/legacy-monster-methods/SKILL.md +444 -0
  72. package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -0
  73. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -0
  74. package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -0
  75. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -0
  76. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -0
  77. package/kit/skills/llm-as-dependency/SKILL.md +436 -0
  78. package/kit/skills/load-shedding-graceful-degradation/SKILL.md +396 -0
  79. package/kit/skills/pre-refactor-characterization/SKILL.md +421 -0
  80. package/kit/skills/production-readiness-review/SKILL.md +305 -0
  81. package/kit/skills/release-engineering/SKILL.md +367 -0
  82. package/kit/skills/retry-strategies/SKILL.md +372 -0
  83. package/kit/skills/sre-risk-management/SKILL.md +221 -0
  84. package/package.json +2 -2
@@ -142,6 +142,17 @@ projeto: {project_id ou "novo"} · tier: {tier} · gerado em {timestamp}
142
142
  `/supabase migration` para iniciar Wave 1.
143
143
  `/supabase rls` para Wave 2.
144
144
  ...
145
+
146
+ ## 9. Observabilidade
147
+ {tabela `obs.events` + audit triggers + SLI views — gerada pelo bloco "Observabilidade integrada"}
148
+
149
+ ## 10. PRR pré-production
150
+ Antes de aceitar tráfego real (≥ 1% de usuários), conduzir Production Readiness Review:
151
+ - Invocar `/sre prr --service <nome>` ou `/prr --feature <descrição>` (cross-ref [prr-conductor](./prr-conductor.md))
152
+ - 6 axes obrigatórios: System Architecture, Instrumentation/Metrics/Monitoring, Emergency Response, Capacity Planning, Change Management, Performance
153
+ - Engagement model: Simple (serviços pequenos), Early Engagement (críticos), Frameworks (built on platform)
154
+ - Gaps P0 = blocker (sem instrumentação básica, sem rollback, sem on-call); Gaps P1 = scheduled tasks
155
+ - Reviewer ≠ time dev — par externo ou SRE conduz (anti auto-PRR)
145
156
  ```
146
157
 
147
158
  Sem preâmbulo. Sem "vou analisar agora". O caller precisa do plano para delegar.
@@ -164,3 +175,41 @@ Schema nasce com observabilidade — não é addon. Este agent SEMPRE projeta:
164
175
  **Output adicionado:** seção "## 9. Observabilidade" no plano com tabela de `obs.events` + audit triggers + SLI views.
165
176
 
166
177
  **Validação ODD** (skill [`observability-driven-development`](../skills/observability-driven-development/SKILL.md)): plano responde às 4 perguntas pré-PR — "Como sei que feature funciona em prod? Como comparo versões? Como sei quem está usando? Como detecto anomalias?"
178
+
179
+ ## Production Readiness Review
180
+
181
+ > Cross-ref canônico: [production-readiness-review](../skills/production-readiness-review/SKILL.md) (cap 32 do livro Google SRE — Evolving SRE Engagement Model). Para conduzir o PRR de fato, delegar para [prr-conductor](./prr-conductor.md).
182
+
183
+ Schema + RLS + Edge Functions Supabase **NÃO são production-ready** só por estarem corretos — production-readiness é evidence-based, com gate explícito em 6 axes. Este agent **SEMPRE** sugere PRR no plano (seção `## 10. PRR pré-production` do output) — sem exceção.
184
+
185
+ ### 6 axes obrigatórios
186
+
187
+ | Axe | O que verifica em contexto Supabase |
188
+ |---|---|
189
+ | **System Architecture** | Redundância (RLS isolamento por tenant; reverso de migrations testado), SPOFs mapeados (single project Supabase = SPOF — branches Pro mitigam), graceful degradation |
190
+ | **Instrumentation / Metrics / Monitoring** | 4 golden signals em Edge Functions (cross-ref [supabase-edge-fn-writer](./supabase-edge-fn-writer.md)), `obs.events` populada, audit hooks ativos, SLI/SLO definidos por jornada crítica |
191
+ | **Emergency Response** | Runbook de incident (RLS broken, schema corrupt, Edge Function 5xx storm), on-call rotation, postmortem template em `.planning/postmortems/` |
192
+ | **Capacity Planning** | Spend Cap configurado, branch billing entendido (Pro), egress projetado, pgvector index size estimate, Edge concurrent invocations limite |
193
+ | **Change Management** | Migrations declarative + reverso testado, RLS policies versionadas em git, Edge Function rollback strategy, supabase functions deploy --import-map idempotente |
194
+ | **Performance** | Load test report (RPS sustentado), p99 latency baseline, RLS policy explain plan (sem seq scan em filtro), index coverage |
195
+
196
+ ### 3 engagement models (escolher conforme criticidade)
197
+
198
+ - **Simple PRR** — para serviços internos / dogfooding / staging-only. Checklist com signoff Eng Lead. Custo baixo, cobertura básica.
199
+ - **Early Engagement** — para serviços tier-1 (production-bound, user-facing, paid tier). PRR conduzido por SRE/external com 6 axes review profundo. **Default para Edge Functions user-facing**.
200
+ - **Frameworks / SRE Platform** — para múltiplos serviços built on top de plataforma comum (ex: framework interno que outros times usam). PRR uma vez por plataforma, depois auto-herança para serviços novos.
201
+
202
+ ### Quando re-rodar PRR
203
+
204
+ - Após mudança maior (rewrite, novo dependency externo, RPS 10×, nova RLS strategy)
205
+ - Antes de aumentar tráfego cross-tier (free → paid → enterprise)
206
+ - Re-run anual mesmo sem mudança (entropia operacional)
207
+
208
+ > **PRR NÃO é one-shot** — statement "passou PRR uma vez em 2024" não é evidence em 2026.
209
+
210
+ ### Anti-patterns prevenidos
211
+
212
+ - Auto-PRR pelo time dev → SEMPRE par externo ou SRE conduz (eyes-on-code novos)
213
+ - "Deploy primeiro, PRR depois" → SEMPRE PRR ANTES de aceitar tráfego real (≥ 1% users)
214
+ - Pular axe (ex: ignorar Capacity Planning porque "feature é small") → SEMPRE 6 axes; pular 1 = aprovação inválida (lacuna oculta vira incident em 6 meses)
215
+ - "Acreditamos que está pronto" → SEMPRE evidence-based (load test report, runbook URL, dashboard link)
@@ -23,6 +23,18 @@ Você é o Edge Function writer Supabase. Recebe descrição de função (endpoi
23
23
 
24
24
  Edge Functions têm pegadinhas específicas do Deno runtime que diferem de Node: bare specifiers quebram, env vars têm nomes pre-populados, file writes só em `/tmp`, multi-rota precisa de prefix. Este agent garante que cada função seguirá essas regras desde o primeiro commit.
25
25
 
26
+ **v1.12 — Adicional Legacy:** Edge Functions são **canônicas para o "API-only application" pattern** (cap 15 livro Feathers, modernizado). Quando este agent escreve Edge Function que wrappar API externa (Stripe/OpenAI/Twilio/etc), aplica skill [`legacy-api-only-applications`](../skills/legacy-api-only-applications/SKILL.md) — adapter pattern com interface mínima testável + anti-corruption layer + fake provider para tests. Quando detecta uso de LLM client (OpenAI/Anthropic), aplica skill [`llm-as-dependency`](../skills/llm-as-dependency/SKILL.md) — LLMProvider interface + adapter por vendor + FakeLLMProvider. Por padrão, este agent oferece **payload capture pattern** (skill [`pre-refactor-characterization`](../skills/pre-refactor-characterization/SKILL.md) Pattern 7) — instrumentação dedicada controlada por env `CAPTURE_PAYLOADS` para captura de fixtures reais via `mcp__supabase__get_logs`.
27
+
28
+ **v1.11 — Adicional SRE Resilience:** Toda Edge Function gerada inclui por padrão **defesas de cascade** (skills `cascading-failures`, `retry-strategies`, `load-shedding-graceful-degradation`):
29
+
30
+ 1. **Timeout em chamadas externas** — `AbortSignal.timeout(2000)` por default
31
+ 2. **Retry com full jitter** — `delayMs = Math.random() * baseMs * 2^attempt`; max 3 retries; cap 30s
32
+ 3. **Deadline propagation** — handler parsea `x-deadline-ms` header e passa downstream
33
+ 4. **Server-side load shedding** — `LoadShedder` em `_shared/load-shedder.ts`; 503 + Retry-After quando saturated
34
+ 5. **Idempotency key** — em writes; gerada via UUID se cliente não enviar
35
+
36
+ Sem flag explícita, esses patterns são incluídos no template de Edge Function nova. Para legacy (Edge Functions já escritas), invocar `/auditar-cascading <fn>` + `/load-shedding <fn>` para retrofit.
37
+
26
38
  ## Inputs esperados (do caller)
27
39
 
28
40
  - `function_name`: nome da função (kebab-case, ex: `process-emails`, `generate-embeddings`)
@@ -196,6 +208,106 @@ Edge Function nasce instrumentada com OTel — não é addon. Beneficia mais que
196
208
 
197
209
  **Output adicionado:** template completo de Edge Function inclui SDK setup + span wrapper + propagação outbound + classificador de error.type. ODD-compliant (4 perguntas pré-PR endereçadas).
198
210
 
211
+ ## Four Golden Signals
212
+
213
+ > Cross-ref canônico: [four-golden-signals](../skills/four-golden-signals/SKILL.md) (cap 6 do livro Google SRE — Monitoring Distributed Systems). Para retro-instrumentar Edge Function existente, delegar para [golden-signals-instrumenter](./golden-signals-instrumenter.md).
214
+
215
+ Edge Function user-facing nasce com os 4 sinais dourados — não é addon. O bloco `## Observabilidade integrada` acima cobre OTel SDK + spans + propagation; este bloco especifica os **4 instrumentos canônicos** que o template gerado SEMPRE inclui:
216
+
217
+ | Signal | Instrumento | Dimensão | Valor padrão |
218
+ |---|---|---|---|
219
+ | **Latency** | `meter.createHistogram('http_request_duration_ms')` com `explicitBucketBoundaries: [1,2,5,10,25,50,100,250,500,1000,2500,5000,10000,30000]` | `result=success\|error` (separar success de erro) | Bucketing exponencial captura long tail sem cardinality explosion |
220
+ | **Traffic** | `meter.createCounter('http_requests_total')` | `endpoint`, `http_method` | Incrementado antes de processar request |
221
+ | **Errors** | `meter.createCounter('http_errors_total')` | `error.type` enum (5-15 valores: `timeout\|validation\|auth\|rate_limit\|db\|provider_down\|...`) — **nunca** `error.message` (cardinalidade explode) | Incrementado em catch + path 4xx/5xx |
222
+ | **Saturation** | `meter.createObservableGauge('saturation_pct')` com callback que lê estado real | resource-specific: `connection_pool` (pg) / `concurrency_limit` (Edge runtime) / `egress_bandwidth` / `cache_memory` | % do recurso mais escasso identificado ANTES de instrumentar |
223
+
224
+ ### Snippet canônico — adicionado ao topo do `index.ts` gerado
225
+
226
+ ```ts
227
+ // PT-BR: 4 golden signals — instrumentação mínima universal
228
+ import { metrics } from 'npm:@opentelemetry/api@1.9.0'
229
+ const meter = metrics.getMeter('<function_name>')
230
+
231
+ // 1. LATENCY — histogram bucketed exponencial
232
+ const latencyHistogram = meter.createHistogram('http_request_duration_ms', {
233
+ description: 'Edge function latency split by result (success vs error)',
234
+ unit: 'ms',
235
+ advice: { explicitBucketBoundaries: [1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000] }
236
+ })
237
+
238
+ // 2. TRAFFIC — counter de requests recebidos
239
+ const trafficCounter = meter.createCounter('http_requests_total', {
240
+ description: 'Total HTTP requests received by edge function'
241
+ })
242
+
243
+ // 3. ERRORS — counter por error.type (NUNCA error.message — cardinalidade)
244
+ const errorsCounter = meter.createCounter('http_errors_total', {
245
+ description: 'Edge function errors by error.type enum'
246
+ })
247
+
248
+ // 4. SATURATION — gauge do recurso mais escasso (callback lê estado real)
249
+ // PT-BR: para Edge Function default, saturation = concurrency_limit_used %
250
+ // Substituir callback conforme recurso identificado (db pool, queue, cache)
251
+ meter.createObservableGauge('saturation_pct', {
252
+ description: 'Saturation of scarcest resource — function-specific'
253
+ }).addCallback((result) => {
254
+ // PT-BR: callback canônico — ler estado real (ex: SELECT count(*) FROM pg_stat_activity)
255
+ // Aqui placeholder: 0 < value < 1
256
+ result.observe(getSaturationPct()) // implementar conforme resource
257
+ })
258
+ ```
259
+
260
+ ### Wrapping no handler
261
+
262
+ ```ts
263
+ Deno.serve(async (req: Request) => {
264
+ const start = performance.now()
265
+ const endpoint = new URL(req.url).pathname
266
+ trafficCounter.add(1, { endpoint, http_method: req.method })
267
+
268
+ try {
269
+ const response = await handle(req)
270
+ latencyHistogram.record(performance.now() - start, {
271
+ endpoint,
272
+ result: response.ok ? 'success' : 'error',
273
+ })
274
+ if (!response.ok) {
275
+ errorsCounter.add(1, { endpoint, 'error.type': classifyError(response) })
276
+ }
277
+ return response
278
+ } catch (err) {
279
+ latencyHistogram.record(performance.now() - start, { endpoint, result: 'error' })
280
+ errorsCounter.add(1, { endpoint, 'error.type': classifyError(err) })
281
+ throw err
282
+ }
283
+ })
284
+
285
+ // PT-BR: classifyError DEVE retornar enum fechado, não err.message
286
+ function classifyError(e: unknown): string {
287
+ if (e instanceof TimeoutError) return 'timeout'
288
+ if (e instanceof ValidationError) return 'validation'
289
+ if (e instanceof AuthError) return 'auth'
290
+ // ... 5-15 valores no total
291
+ return 'unknown'
292
+ }
293
+ ```
294
+
295
+ ### Saturation por tipo de Edge Function
296
+
297
+ | Tipo de função | Recurso mais escasso | Implementação típica |
298
+ |---|---|---|
299
+ | API simples (GET/POST com leitura DB) | `pg_pool` connections used | `select count(*) from pg_stat_activity where state = 'active'` |
300
+ | RAG / embeddings | `concurrency_limit` (provider externo) | counter de requests in-flight |
301
+ | Email / queue consumer (cron → pgmq) | `pgmq.queue_length` | `select msg_count from pgmq.metrics_<queue>` |
302
+ | Storage I/O heavy (uploads grandes) | `egress_bandwidth` | bytes-out tracker em window |
303
+
304
+ ### Anti-patterns prevenidos
305
+
306
+ - Errors counter usando `error.type = err.message` → SEMPRE enum fechado (5-15 valores)
307
+ - Latency mistura success + error → SEMPRE `result` dimension separa
308
+ - Mean latency em vez de histogram → SEMPRE histogram com percentis derivados em backend
309
+ - Saturation genérico (CPU%) sem identificar recurso real → SEMPRE escolher recurso scarcest da função
310
+
199
311
  ## Ver também
200
312
 
201
313
  - [supabase-edge-functions](../skills/supabase-edge-functions/SKILL.md) — base de conhecimento canônica
@@ -205,3 +317,5 @@ Edge Function nasce instrumentada com OTel — não é addon. Beneficia mais que
205
317
  - [distributed-tracing](../skills/distributed-tracing/SKILL.md) — context propagation
206
318
  - [structured-events](../skills/structured-events/SKILL.md) — campos canônicos
207
319
  - [observability-driven-development](../skills/observability-driven-development/SKILL.md) — 4 perguntas pré-PR
320
+ - [four-golden-signals](../skills/four-golden-signals/SKILL.md) — 4 sinais canônicos (Latency, Traffic, Errors, Saturation) cap 6 livro Google SRE
321
+ - [golden-signals-instrumenter](./golden-signals-instrumenter.md) — agent que retro-instrumenta Edge Functions existentes com os 4 signals
@@ -172,3 +172,83 @@ Toda migration emite evento estruturado e cria audit hooks por default — não
172
172
  3. **Atributos canônicos** em qualquer função criada: `set search_path = ''` + comments com `result.success`, `error.type` enum esperado (skill [`structured-events`](../skills/structured-events/SKILL.md)).
173
173
 
174
174
  **Output adicionado:** seção "## Audit hooks" + "## Migration event emit" no SQL gerado, comentadas em PT-BR.
175
+
176
+ ## Alerta toil — automação via pg_cron
177
+
178
+ > Cross-ref canônico: [eliminating-toil](../skills/eliminating-toil/SKILL.md) (cap 5 do livro Google SRE — Eliminating Toil). Para auditoria sistemática de toil em todo o repo, delegar para [toil-auditor](./toil-auditor.md).
179
+
180
+ Migrations SQL executadas **manualmente em cadência regular** (rebuild índice, VACUUM, REFRESH MATERIALIZED VIEW, ANALYZE) são toil canônico — passam todos os 6 critérios: manual, repetitivo, automatizável, tático, sem valor durável, escala linear. Este agent **detecta padrões de toil** ao escrever migration e **alerta proativamente** sugerindo automação via `pg_cron`.
181
+
182
+ ### 6 critérios — quando uma migration é toil-prone
183
+
184
+ Migration descreve operação que será re-executada > 1× = toil-prone. Aplicar 6 critérios da skill `eliminating-toil`:
185
+
186
+ | Critério | Pergunta | Sinal de toil |
187
+ |---|---|---|
188
+ | 1. Manual | Operador roda `psql` ou aplica migration "quando lembra"? | Sim |
189
+ | 2. Repetitivo | Já foi executada 3+ vezes em milestones diferentes? | Sim |
190
+ | 3. Automatizável | `pg_cron` consegue agendar sem julgamento humano? | Sim |
191
+ | 4. Tático | Reage a sintoma (lentidão, bloat, stale view) sem planejar? | Sim |
192
+ | 5. Sem valor durável | Não cria asset permanente — só "limpa" estado | Sim |
193
+ | 6. Escala linear | Mais users / mais dados = mais frequência manual | Sim |
194
+
195
+ Se TODOS os 6 = sim → **toil**. Bloquear migration manual recorrente; oferecer alternativa via `pg_cron`.
196
+
197
+ ### Padrões SQL canônicos que SEMPRE disparam alerta toil
198
+
199
+ | Operação manual | Por quê é toil | Automação canônica |
200
+ |---|---|---|
201
+ | `REINDEX TABLE x` recorrente (a cada N semanas) | Rebuild de bloat de índice é tático, sem valor durável, repetitivo | `select cron.schedule('reindex_x', '0 3 * * 0', $$reindex table x$$);` (semanal 3am) |
202
+ | `VACUUM ANALYZE x` manual | autovacuum não está acompanhando — sintoma de tuning, não fix manual | Tunar `autovacuum_vacuum_scale_factor` para tabela específica + `pg_cron` se necessário |
203
+ | `REFRESH MATERIALIZED VIEW x` manual | Stale view detectada por user reclamação ou alert | `select cron.schedule('refresh_x', '*/30 * * * * *', $$refresh materialized view concurrently x$$);` |
204
+ | `ANALYZE` em tabela após bulk insert manual | Estatísticas desatualizadas após ETL — bem conhecido | Trigger AFTER INSERT/COPY com `analyze` no fim do batch, ou `pg_cron` pós-ETL |
205
+ | `delete from logs where created_at < now() - interval '90d'` manual recorrente | Retention manual = toil clássico | `select cron.schedule('purge_logs', '0 4 * * *', $$delete from logs where ...$$);` |
206
+ | `dump + restore` periódico para estatísticas / planos cache | Operação repetitiva sem valor permanente | `pg_cron` job ou `pg_stat_reset_*()` calls automatizadas |
207
+
208
+ ### Snippet canônico — converter manual em pg_cron
209
+
210
+ ```sql
211
+ -- PT-BR: ANTES — toil (operador roda manualmente)
212
+ -- $ psql -c 'reindex table heavy_table;' ← repetir a cada 2 semanas
213
+
214
+ -- PT-BR: DEPOIS — automação via pg_cron (necessita extension pg_cron habilitada)
215
+ create extension if not exists pg_cron;
216
+
217
+ select cron.schedule(
218
+ 'reindex_heavy_table_biweekly',
219
+ '0 3 1,15 * *', -- 3am dias 1 e 15
220
+ $$ reindex table public.heavy_table $$
221
+ );
222
+
223
+ -- PT-BR: monitor — falha em job pg_cron emite linha em cron.job_run_details
224
+ -- alimentar alerta SLO se job falha 3+ vezes seguidas
225
+ ```
226
+
227
+ ### Quando NÃO automatizar (não é toil)
228
+
229
+ - **Migration de schema (DDL one-shot)** — `create table`, `alter table add column` são project work, não toil. Não recorrentes.
230
+ - **Backfill data único** — `update orders set status = ...` aplicado 1× para corrigir bug é grungy work, não toil.
231
+ - **Rebuild que requer julgamento** — `reindex` que requer escolher hora baseada em load patterns variáveis, ou que precisa coordenação com release. Mantém manual mas documenta runbook.
232
+
233
+ ### Output do agent — adicionado ao SQL gerado
234
+
235
+ Quando o agent detecta que a migration descreve operação toil-prone (regex em DDL: `reindex|vacuum|refresh materialized|delete from .* interval`), adiciona comentário-alerta no header do arquivo SQL gerado:
236
+
237
+ ```sql
238
+ /*
239
+ ⚠ TOIL ALERT — esta operação parece recorrente.
240
+
241
+ Se será executada em cadência regular, considere automação via pg_cron:
242
+ select cron.schedule('<job_name>', '<schedule>', $$ <sql> $$);
243
+
244
+ Cross-ref: kit/skills/eliminating-toil/SKILL.md (6 critérios canônicos)
245
+ kit/agents/toil-auditor.md (audit sistemático para repo todo)
246
+ */
247
+ ```
248
+
249
+ ### Anti-patterns prevenidos
250
+
251
+ - "Roda quando der" runbook → SEMPRE pg_cron + monitoring de falha do job
252
+ - `pg_cron` schedule mas sem alerta de falha → SEMPRE incluir SLO em `cron.job_run_details` (% sucesso 30d)
253
+ - Automação parcial (script humano-iniciado) → ainda é toil (humano pressiona botão); preferir cron.schedule completo
254
+ - Migration manual recorrente "porque é só uma vez por mês" → 12×/ano = toil, regra ≤ 50% se acumular vários "só um por mês"
@@ -249,6 +249,160 @@ Upload events são quentes em custo (egress + storage) e em UX (lentidão de upl
249
249
 
250
250
  **Output adicionado:** seção "## Observability hooks" com snippet de upload/download wrapper.
251
251
 
252
+ ## Saturation signal — bucket size + quota
253
+
254
+ > Cross-ref canônico: [four-golden-signals](../skills/four-golden-signals/SKILL.md) (cap 6 do livro Google SRE — Monitoring Distributed Systems). Para retro-instrumentar storage existente com os 4 signals, delegar para [golden-signals-instrumenter](./golden-signals-instrumenter.md).
255
+
256
+ Storage tem o **recurso mais escasso explícito**: o quota do plano (Free 1 GB, Pro 100 GB, Team 1 TB, etc.). Sem signal de saturation, time descobre quota exhaustion via incident (uploads falham silenciosamente em UX) — **anti-pattern clássico** de white-box monitoring sem detecção precoce. O bloco `## Observabilidade integrada` acima cobre Latency / Traffic / Errors (3 signals); este bloco completa com **Saturation** — o 4º signal canônico.
257
+
258
+ ### Saturation = bucket size ÷ quota plan
259
+
260
+ | Plano | Quota total | Threshold ALERT (yellow) | Threshold PAGE (red) |
261
+ |---|---|---|---|
262
+ | Free | 1 GB | 80% (800 MB) | 95% (950 MB) |
263
+ | Pro | 100 GB | 80% (80 GB) | 95% (95 GB) |
264
+ | Team | 1 TB | 80% (800 GB) | 95% (950 GB) |
265
+ | Enterprise | custom | custom | custom |
266
+
267
+ ### Signal 1 — Gauge: bucket size atual (bytes)
268
+
269
+ `ObservableGauge` (push periódico via callback) mede tamanho real de cada bucket. Callback consulta `storage.objects` agregado:
270
+
271
+ ```ts
272
+ // PT-BR: 4º signal — saturation (gauge de bucket size em bytes)
273
+ import { metrics } from 'npm:@opentelemetry/api@1.9.0'
274
+ const meter = metrics.getMeter('supabase-storage')
275
+
276
+ meter.createObservableGauge('storage_bucket_bytes', {
277
+ description: 'Tamanho atual em bytes por bucket — saturation signal',
278
+ unit: 'bytes',
279
+ }).addCallback(async (result) => {
280
+ // PT-BR: query agregada (rodar via service-role client em cron)
281
+ const sizes = await supabaseAdmin.rpc('storage_bucket_sizes_bytes')
282
+ // expected: [{ bucket_id: 'avatars', total_bytes: 12345678 }, ...]
283
+ for (const row of sizes ?? []) {
284
+ result.observe(row.total_bytes, { 'bucket.id': row.bucket_id })
285
+ }
286
+ })
287
+
288
+ meter.createObservableGauge('storage_saturation_pct', {
289
+ description: 'Saturation = bucket size / quota plan — % do quota usado',
290
+ unit: '1', // ratio (0..1)
291
+ }).addCallback(async (result) => {
292
+ const sizes = await supabaseAdmin.rpc('storage_bucket_sizes_bytes')
293
+ const QUOTA_BYTES = Number(Deno.env.get('SUPABASE_PLAN_QUOTA_BYTES') ?? 1_000_000_000) // default Free
294
+ for (const row of sizes ?? []) {
295
+ result.observe(row.total_bytes / QUOTA_BYTES, { 'bucket.id': row.bucket_id })
296
+ }
297
+ })
298
+ ```
299
+
300
+ SQL helper para o callback:
301
+
302
+ ```sql
303
+ -- PT-BR: function que retorna bytes por bucket — chamada por callback OTel
304
+ create or replace function public.storage_bucket_sizes_bytes()
305
+ returns table (bucket_id text, total_bytes bigint)
306
+ language sql
307
+ security definer
308
+ set search_path = ''
309
+ as $$
310
+ select bucket_id, coalesce(sum((metadata->>'size')::bigint), 0) as total_bytes
311
+ from storage.objects
312
+ group by bucket_id;
313
+ $$;
314
+ ```
315
+
316
+ ### Signal 2 — Counter: quota near-exhaustion events
317
+
318
+ `Counter` incrementa a cada upload que **detecta** approach a quota threshold (80%, 95%). Permite contar eventos críticos para alerting:
319
+
320
+ ```ts
321
+ // PT-BR: counter incrementado em cada upload
322
+ const quotaWarnings = meter.createCounter('storage_quota_warnings_total', {
323
+ description: 'Counter de eventos onde upload aproxima quota — alimentar alert SLO',
324
+ })
325
+
326
+ export async function uploadInstrumented(file: File, filename: string) {
327
+ const supabase = createClient()
328
+ const { data: { user } } = await supabase.auth.getUser()
329
+ if (!user) throw new Error('not authenticated')
330
+
331
+ const path = `${user.id}/${filename}`
332
+
333
+ // PT-BR: pre-check — saturation atual antes de upload
334
+ const sizes = await supabaseAdmin.rpc('storage_bucket_sizes_bytes')
335
+ const bucketSize = sizes?.find(s => s.bucket_id === '<bucket_name>')?.total_bytes ?? 0
336
+ const QUOTA = Number(Deno.env.get('SUPABASE_PLAN_QUOTA_BYTES') ?? 1_000_000_000)
337
+ const saturation = bucketSize / QUOTA
338
+
339
+ if (saturation >= 0.95) {
340
+ quotaWarnings.add(1, { 'bucket.id': '<bucket_name>', threshold: '95pct' })
341
+ } else if (saturation >= 0.80) {
342
+ quotaWarnings.add(1, { 'bucket.id': '<bucket_name>', threshold: '80pct' })
343
+ }
344
+
345
+ const { data, error } = await supabase.storage
346
+ .from('<bucket_name>')
347
+ .upload(path, file, { upsert: true })
348
+
349
+ if (error) throw error
350
+ return data.path
351
+ }
352
+ ```
353
+
354
+ ### Cron schedule sugerido
355
+
356
+ Saturation gauge não precisa rodar em cada request — agendar leitura via `pg_cron` (ou OTel SDK polling interval = 60s) é suficiente:
357
+
358
+ ```sql
359
+ -- PT-BR: refresh saturation cache a cada 60s para gauge OTel
360
+ create materialized view if not exists obs.storage_saturation as
361
+ select bucket_id, sum((metadata->>'size')::bigint) as total_bytes, now() as captured_at
362
+ from storage.objects
363
+ group by bucket_id;
364
+
365
+ select cron.schedule(
366
+ 'refresh_storage_saturation',
367
+ '* * * * *', -- a cada 1 min
368
+ $$ refresh materialized view concurrently obs.storage_saturation $$
369
+ );
370
+ ```
371
+
372
+ ### Alert SLO sobre saturation
373
+
374
+ Saturation alimenta SLO event-based — não threshold direto:
375
+
376
+ ```yaml
377
+ # PT-BR: SLO sobre quota — % de tempo em yellow ou worse
378
+ slo:
379
+ name: storage_quota_healthy
380
+ target: 0.99 # 99% do tempo em < 80% quota
381
+ window: 30d_sliding
382
+ sli:
383
+ type: event_based
384
+ good_event:
385
+ saturation_pct: { lt: 0.80 }
386
+ bad_event:
387
+ saturation_pct: { gte: 0.80 }
388
+ ```
389
+
390
+ ### Output do agent — adicionado ao SQL/código gerado
391
+
392
+ Quando agent gera bucket privado novo, **sempre inclui**:
393
+ 1. Function SQL `storage_bucket_sizes_bytes()` (uma vez por projeto)
394
+ 2. Materialized view `obs.storage_saturation` + pg_cron refresh job
395
+ 3. Snippet OTel ObservableGauge no código client wrapper
396
+ 4. Counter `storage_quota_warnings_total` no upload wrapper
397
+ 5. SLO `storage_quota_healthy` em `.planning/slos/<bucket>.yaml`
398
+
399
+ ### Anti-patterns prevenidos
400
+
401
+ - Saturation = "% disco do servidor" → SEMPRE saturation = % quota plan (recurso correto)
402
+ - Threshold direto em alerta CPU/memory para capacity → SEMPRE SLO event-based sobre saturation_pct
403
+ - Polling de bucket size em cada request → SEMPRE materialized view + pg_cron refresh + OTel polling 60s
404
+ - Plan quota hardcoded → SEMPRE env var `SUPABASE_PLAN_QUOTA_BYTES` (varia por plano, pode ser sobrescrita em test)
405
+
252
406
  ## Ver também
253
407
 
254
408
  - [supabase-storage](../skills/supabase-storage/SKILL.md) — base de conhecimento canônica
@@ -256,3 +410,5 @@ Upload events são quentes em custo (egress + storage) e em UX (lentidão de upl
256
410
  - [supabase-auth-ssr](../skills/supabase-auth-ssr/SKILL.md) — usuário autenticado obtém `auth.uid()`
257
411
  - [structured-events](../skills/structured-events/SKILL.md) — campos canônicos para upload/download events
258
412
  - [telemetry-sampling](../skills/telemetry-sampling/SKILL.md) *(Phase 34)* — head-based sampling por size_bytes
413
+ - [four-golden-signals](../skills/four-golden-signals/SKILL.md) — 4 sinais canônicos (Latency, Traffic, Errors, Saturation) cap 6 livro Google SRE — saturation = bucket size / quota plan
414
+ - [golden-signals-instrumenter](./golden-signals-instrumenter.md) — agent que retro-instrumenta storage existente com os 4 signals