@luanpdd/kit-mcp 1.34.0 → 1.36.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 (118) hide show
  1. package/README.md +1 -1
  2. package/bin/cli.js +2 -2
  3. package/bin/mcp.js +6 -6
  4. package/bin/ui.js +74 -74
  5. package/gates/ai-prompt-stability.md +120 -120
  6. package/gates/budget-description.md +68 -68
  7. package/gates/confidence.md +29 -29
  8. package/gates/dependency-check.md +33 -33
  9. package/gates/dept-cycle-prevention.md +179 -179
  10. package/gates/golden-signals-coverage.md +133 -133
  11. package/gates/legacy-refactor-safety.md +178 -178
  12. package/gates/multi-tenant-rls-coverage.md +102 -102
  13. package/gates/no-personal-uuid.md +72 -72
  14. package/gates/obs-agents-mcp-supabase.md +86 -86
  15. package/gates/obs-skills-frontmatter.md +76 -76
  16. package/gates/observability-coverage.md +151 -151
  17. package/gates/omm-no-regression.md +83 -83
  18. package/gates/postmortem-template-required.md +127 -127
  19. package/gates/prr-checklist-coverage.md +128 -128
  20. package/gates/regression.md +32 -32
  21. package/gates/release-pipeline-policy.md +132 -132
  22. package/gates/secrets-scan.md +33 -33
  23. package/gates/service-role-not-in-user-facing.md +113 -113
  24. package/gates/skill-must-include.md +71 -71
  25. package/gates/sync-idempotent.md +62 -62
  26. package/gates/verify-phase-goal.md +34 -34
  27. package/kit/agents/designer-ui.md +216 -216
  28. package/kit/agents/workflow-generator.md +537 -0
  29. package/kit/commands/adicionar-backlog.md +1 -1
  30. package/kit/commands/adicionar-fase.md +1 -1
  31. package/kit/commands/adicionar-tarefa.md +1 -1
  32. package/kit/commands/auditar-observabilidade.md +103 -103
  33. package/kit/commands/auditar-toil.md +129 -129
  34. package/kit/commands/caracterizar-prompt.md +195 -195
  35. package/kit/commands/criar-workflow.md +158 -0
  36. package/kit/commands/definir-perfil.md +1 -1
  37. package/kit/commands/definir-slo.md +108 -108
  38. package/kit/commands/fio.md +1 -1
  39. package/kit/commands/golden-signals.md +142 -142
  40. package/kit/commands/instrumentar-fase.md +200 -200
  41. package/kit/commands/investigar-producao.md +162 -162
  42. package/kit/commands/observabilidade.md +118 -118
  43. package/kit/commands/postmortem.md +179 -179
  44. package/kit/commands/prr.md +205 -205
  45. package/kit/commands/publicar-rapido.md +207 -207
  46. package/kit/commands/risk-budget.md +220 -220
  47. package/kit/commands/sre.md +230 -230
  48. package/kit/file-manifest.json +5 -2
  49. package/kit/framework/references/output-style.md +22 -22
  50. package/kit/hooks/post-apply-migration.js +199 -199
  51. package/kit/hooks/sidecar-tool-publisher.js +210 -210
  52. package/kit/skills/_shared-dados-distribuidos/glossary.md +224 -224
  53. package/kit/skills/_shared-legacy/glossary.md +389 -389
  54. package/kit/skills/_shared-multi-tenant/glossary.md +186 -186
  55. package/kit/skills/_shared-observability/glossary.md +396 -396
  56. package/kit/skills/_shared-sre/glossary.md +712 -712
  57. package/kit/skills/_shared-supabase/glossary.md +234 -234
  58. package/kit/skills/blameless-postmortems/SKILL.md +340 -340
  59. package/kit/skills/burn-rate-alerting/SKILL.md +258 -258
  60. package/kit/skills/cascading-failures/SKILL.md +311 -311
  61. package/kit/skills/core-analysis-loop/SKILL.md +352 -352
  62. package/kit/skills/distributed-tracing/SKILL.md +362 -362
  63. package/kit/skills/dynamic-workflow-authoring/SKILL.md +327 -0
  64. package/kit/skills/eliminating-toil/SKILL.md +243 -243
  65. package/kit/skills/event-based-slos/SKILL.md +296 -296
  66. package/kit/skills/four-golden-signals/SKILL.md +314 -314
  67. package/kit/skills/hermetic-builds/SKILL.md +323 -323
  68. package/kit/skills/legacy-monster-methods/SKILL.md +444 -444
  69. package/kit/skills/llm-as-dependency/SKILL.md +436 -436
  70. package/kit/skills/load-shedding-graceful-degradation/SKILL.md +396 -396
  71. package/kit/skills/observability-driven-development/SKILL.md +315 -315
  72. package/kit/skills/observability-maturity-model/SKILL.md +222 -222
  73. package/kit/skills/opentelemetry-standard/SKILL.md +351 -351
  74. package/kit/skills/production-readiness-review/SKILL.md +305 -305
  75. package/kit/skills/release-engineering/SKILL.md +367 -367
  76. package/kit/skills/retry-strategies/SKILL.md +372 -372
  77. package/kit/skills/sre-risk-management/SKILL.md +221 -221
  78. package/kit/skills/structured-events/SKILL.md +265 -265
  79. package/kit/skills/supabase-cron-queues/SKILL.md +275 -275
  80. package/kit/skills/supabase-database-functions/SKILL.md +332 -332
  81. package/kit/skills/supabase-declarative-schema/SKILL.md +183 -183
  82. package/kit/skills/supabase-pgvector-rag/SKILL.md +253 -253
  83. package/kit/skills/supabase-postgres-style/SKILL.md +138 -138
  84. package/kit/skills/supabase-storage/SKILL.md +234 -234
  85. package/kit/skills/telemetry-pipelines/SKILL.md +259 -259
  86. package/kit/skills/telemetry-sampling/SKILL.md +256 -256
  87. package/kit/skills/ui-anti-padroes-ia/SKILL.md +261 -261
  88. package/kit/skills/ui-contexto-produto/SKILL.md +248 -248
  89. package/kit/skills/ui-cor-estrategia/SKILL.md +213 -213
  90. package/kit/skills/ui-critica-auditoria/SKILL.md +260 -260
  91. package/kit/skills/ui-motion-funcional/SKILL.md +264 -264
  92. package/kit/skills/ui-ritmo-espacial/SKILL.md +259 -259
  93. package/kit/skills/ui-tipografia/SKILL.md +211 -211
  94. package/package.json +1 -1
  95. package/src/cli/index.js +1114 -1114
  96. package/src/cli/render.js +194 -194
  97. package/src/cli/upgrade-check.js +135 -135
  98. package/src/core/error-redaction.js +76 -76
  99. package/src/core/failures.js +153 -153
  100. package/src/core/gate-runner.js +205 -205
  101. package/src/core/gates.js +82 -82
  102. package/src/core/logger.js +170 -170
  103. package/src/core/manifest-verify.js +174 -174
  104. package/src/core/metrics.js +268 -268
  105. package/src/core/notify.js +60 -60
  106. package/src/core/path-safety.js +141 -141
  107. package/src/core/replays.js +120 -120
  108. package/src/core/ui.js +185 -185
  109. package/src/mcp-server/install.js +149 -149
  110. package/src/mcp-server/roots.js +124 -124
  111. package/src/ui/auto-spawn.js +113 -113
  112. package/src/ui/browser.js +78 -78
  113. package/src/ui/client.js +130 -130
  114. package/src/ui/events.js +65 -65
  115. package/src/ui/lockfile.js +191 -191
  116. package/src/ui/port.js +67 -67
  117. package/src/ui/server.js +547 -547
  118. package/src/ui/wrapper.js +129 -129
@@ -1,275 +1,275 @@
1
- ---
2
- name: supabase-cron-queues
3
- description: Use ao orquestrar background jobs — pg_cron + pgmq + pg_net pattern cron → pgmq → Edge Function. Sem dep externa. Postgres 15.6.1.143+.
4
- ---
5
-
6
- # Supabase — Cron + Queues (background jobs)
7
-
8
- ## Quando usar
9
-
10
- LLM carrega esta skill quando implementar background jobs, scheduled tasks ou queues em Supabase **sem dependência externa** (Inngest, Trigger.dev, etc.). Trigger phrases:
11
-
12
- - "pg_cron", "supabase cron job"
13
- - "pgmq", "Postgres Message Queue"
14
- - "pg_net", "HTTP from database"
15
- - "background job Supabase"
16
- - "scheduled task Supabase"
17
-
18
- ## Regras absolutas
19
-
20
- - **Extensions necessárias:**
21
- - **`pg_cron`** — jobs scheduled (cron syntax)
22
- - **`pgmq`** — Postgres Message Queue (requer Postgres **15.6.1.143+**)
23
- - **`pg_net`** — HTTP requests do banco (recomendado v0.10.0+)
24
- - **Pattern canônico:** **`cron → pgmq → Edge Function`** — `pg_cron` enfileira mensagens em `pgmq`, Edge Function consome (via cron ou polling).
25
- - **Jobs `pg_cron` curtos** (< 10 min) — jobs longos bloqueiam scheduler. Para jobs longos, enfileire em `pgmq` e processe via Edge Function.
26
- - **`pgmq.send`** para enfileirar; **`pgmq.read` + `pgmq.archive`** para consumir. Visibility timeout previne double processing.
27
- - **`pg_net` é async** — `net.http_post` retorna `request_id`, response chega em `net._http_response`. Não bloqueia caller.
28
- - **Idempotência** — Edge Function consumer deve ser idempotente (mesma mensagem pode ser entregue 2× em retry).
29
- - **Cleanup** — sem `pgmq.archive` ou `pgmq.delete`, mensagem reaparece após visibility timeout (re-processed).
30
-
31
- ## Patterns canônicos
32
-
33
- ### Setup das extensions + criar fila
34
-
35
- ```sql
36
- -- PT-BR: habilitar extensions (uma vez por projeto)
37
- create extension if not exists pg_cron;
38
- create extension if not exists pgmq;
39
- create extension if not exists pg_net;
40
-
41
- -- PT-BR: criar fila pgmq
42
- select pgmq.create('email_jobs');
43
-
44
- -- PT-BR: opcional — criar fila com retention customizado
45
- -- select pgmq.create_partitioned('large_jobs');
46
- ```
47
-
48
- ### Pattern canônico — `cron → pgmq → Edge Function`
49
-
50
- ```sql
51
- -- PT-BR: 1. cron job a cada 5 min enfileira pendências em pgmq
52
- select cron.schedule(
53
- 'enqueue-pending-emails',
54
- '*/5 * * * *', -- a cada 5 min
55
- $$
56
- insert into pgmq.q_email_jobs (message)
57
- select jsonb_build_object(
58
- 'user_id', id,
59
- 'kind', 'reminder',
60
- 'enqueued_at', now()
61
- )
62
- from public.users
63
- where pending_email = true
64
- limit 1000; -- batch limitado
65
- $$
66
- );
67
-
68
- -- PT-BR: 2. cron job a cada minuto despara processamento via Edge Function
69
- select cron.schedule(
70
- 'process-email-queue',
71
- '*/1 * * * *', -- a cada minuto
72
- $$
73
- select net.http_post(
74
- url := 'https://<project-ref>.supabase.co/functions/v1/process-emails',
75
- headers := jsonb_build_object(
76
- 'Content-Type', 'application/json',
77
- 'Authorization', 'Bearer ' || current_setting('supabase.functions_token', true)
78
- ),
79
- body := '{}'::jsonb
80
- );
81
- $$
82
- );
83
- ```
84
-
85
- ### Edge Function consumer — pgmq.read + archive
86
-
87
- ```ts
88
- // supabase/functions/process-emails/index.ts
89
- // PT-BR: consume da fila pgmq, processa, archive
90
- import { createClient } from 'npm:@supabase/supabase-js@2'
91
-
92
- Deno.serve(async () => {
93
- const supabase = createClient(
94
- Deno.env.get('SUPABASE_URL')!,
95
- Deno.env.get('SUPABASE_SECRET_KEYS')!
96
- )
97
-
98
- // PT-BR: pegar até 10 mensagens com visibility timeout 30s
99
- const { data: msgs, error } = await supabase.rpc('pgmq_read', {
100
- queue_name: 'email_jobs',
101
- vt: 30, // visibility timeout em segundos
102
- qty: 10, // máximo por chamada
103
- })
104
-
105
- if (error || !msgs?.length) {
106
- return new Response('no jobs', { status: 200 })
107
- }
108
-
109
- for (const m of msgs) {
110
- try {
111
- // PT-BR: processar mensagem (idempotente!)
112
- await sendEmail(m.message.user_id, m.message.kind)
113
-
114
- // PT-BR: archive remove da fila e move para arquivo
115
- await supabase.rpc('pgmq_archive', {
116
- queue_name: 'email_jobs',
117
- msg_id: m.msg_id,
118
- })
119
- } catch (err) {
120
- // PT-BR: erro — não archive; visibility timeout expira e mensagem reaparece
121
- console.error('processing error', m.msg_id, err)
122
- }
123
- }
124
-
125
- return new Response(`processed ${msgs.length}`)
126
- })
127
- ```
128
-
129
- ### Job cron simples — sem queue (cuidado: < 10 min)
130
-
131
- ```sql
132
- -- PT-BR: ok para tarefas leves e rápidas (cleanup, agregação)
133
- select cron.schedule(
134
- 'cleanup-old-sessions',
135
- '0 3 * * *', -- 3am diário
136
- $$
137
- delete from public.sessions where expires_at < now() - interval '30 days';
138
- $$
139
- );
140
- ```
141
-
142
- ### Listar e remover jobs cron
143
-
144
- ```sql
145
- -- PT-BR: listar todos os jobs
146
- select * from cron.job;
147
-
148
- -- PT-BR: remover job
149
- select cron.unschedule('process-email-queue');
150
- ```
151
-
152
- ### `pg_net.http_post` async
153
-
154
- ```sql
155
- -- PT-BR: dispara HTTP request, retorna request_id imediatamente
156
- select net.http_post(
157
- url := 'https://api.example.com/webhook',
158
- headers := jsonb_build_object('Authorization', 'Bearer xxx'),
159
- body := jsonb_build_object('event', 'task_completed'),
160
- timeout_milliseconds := 5000
161
- );
162
-
163
- -- PT-BR: response chega em net._http_response (consultar depois)
164
- select * from net._http_response order by created desc limit 10;
165
- ```
166
-
167
- ## Anti-patterns
168
-
169
- ### Anti-pattern 1: Job cron longo (> 10 min)
170
-
171
- **Errado:**
172
- ```sql
173
- select cron.schedule(
174
- 'heavy-batch-process',
175
- '0 * * * *',
176
- $$ select pg_sleep(900); ... $$ -- ⚠ 15 min em pg_cron
177
- );
178
- ```
179
-
180
- **Por quê:** `pg_cron` worker bloqueia outros jobs enquanto roda. Se job > 10 min ou trava, scheduler atrasa cascata. Em retry após failure, pode trancar inteiramente.
181
-
182
- **Certo:** cron enfileira; Edge Function processa pesado:
183
- ```sql
184
- -- cron: leve (só enfileira)
185
- select cron.schedule('enqueue-heavy', '0 * * * *', $$
186
- insert into pgmq.q_heavy_jobs (message) select ...;
187
- $$);
188
- -- Edge Function: pesado (consome com timeout próprio)
189
- ```
190
-
191
- ### Anti-pattern 2: HTTP síncrono direto de pg_cron
192
-
193
- **Errado:**
194
- ```sql
195
- select cron.schedule('call-api', '*/1 * * * *', $$
196
- -- ⚠ pg_net é async, mas user pode tentar sync com loops
197
- select net.http_get('https://api.example.com/long');
198
- $$);
199
- ```
200
-
201
- **Por quê:** HTTP requests podem demorar segundos a minutos. Se response demora, próxima execução do cron empilha. Em alta latência, scheduler fica trancado.
202
-
203
- **Certo:** enfileire em pgmq + Edge Function processa:
204
- ```sql
205
- -- cron: enfileira
206
- insert into pgmq.q_api_calls (message) values ('{"endpoint": "/long"}');
207
- -- Edge Function: chama API com timeout próprio + archive
208
- ```
209
-
210
- ### Anti-pattern 3: `pgmq.read` sem `archive` ou `delete`
211
-
212
- **Errado:**
213
- ```ts
214
- const { data: msgs } = await supabase.rpc('pgmq_read', { queue_name: 'jobs', vt: 30, qty: 10 })
215
- for (const m of msgs) {
216
- await processJob(m.message)
217
- // ⚠ esqueceu pgmq_archive
218
- }
219
- ```
220
-
221
- **Por quê:** após visibility timeout (30s), mensagem reaparece — mesmo job rodado novamente. Em loop, leva a re-processing infinito.
222
-
223
- **Certo:**
224
- ```ts
225
- for (const m of msgs) {
226
- try {
227
- await processJob(m.message)
228
- await supabase.rpc('pgmq_archive', { queue_name: 'jobs', msg_id: m.msg_id })
229
- } catch (err) {
230
- // PT-BR: NÃO archive; mensagem retorna após vt para retry
231
- }
232
- }
233
- ```
234
-
235
- ### Anti-pattern 4: Edge Function não-idempotente
236
-
237
- **Errado:**
238
- ```ts
239
- async function processJob(msg) {
240
- await sendEmail(msg.user_id) // ⚠ envia email mesmo se já enviado
241
- await chargeCard(msg.amount) // ⚠ cobra mesmo se já cobrado
242
- }
243
- ```
244
-
245
- **Por quê:** retries entregam mesma mensagem 2×+. Sem idempotência, side effects duplicam — usuário recebe 2 emails ou é cobrado 2×.
246
-
247
- **Certo:** rastreie estado:
248
- ```ts
249
- async function processJob(msg) {
250
- const { data: existing } = await supabase
251
- .from('email_log')
252
- .select('id')
253
- .eq('msg_id', msg.id)
254
- .single()
255
- if (existing) return // já processado
256
- await sendEmail(msg.user_id)
257
- await supabase.from('email_log').insert({ msg_id: msg.id })
258
- }
259
- ```
260
-
261
- ## Padrões Exactly-Once em pgmq (v1.22+)
262
-
263
- > Background jobs em pgmq tendem a duplicate processing em retry/timeout. Padrão canônico (DDIA Ch 11):
264
- > 1. **Dedup table** com `unique(event_id)` — INSERT antes do processamento; falha = já processado.
265
- > 2. **Idempotency key** no handler — mesmo input → mesmo output (sem efeitos colaterais).
266
- > 3. **Transactional outbox** — write DB + event em mesma transação atomic; processador async lê outbox e publica.
267
- >
268
- > Detalhes completos em [`streams-eventos-cdc`](../streams-eventos-cdc/SKILL.md) (v1.22).
269
-
270
- ## Ver também
271
-
272
- - [supabase-edge-functions](../supabase-edge-functions/SKILL.md) — Edge Functions consumindo pgmq
273
- - [supabase-database-functions](../supabase-database-functions/SKILL.md) — funções com `set search_path = ''` chamadas em cron
274
- - [supabase-migrations](../supabase-migrations/SKILL.md) — extensions criadas em migrations
275
- - [glossário](../_shared-supabase/glossary.md) — pg_cron, pgmq, pg_net
1
+ ---
2
+ name: supabase-cron-queues
3
+ description: Use ao orquestrar background jobs — pg_cron + pgmq + pg_net pattern cron → pgmq → Edge Function. Sem dep externa. Postgres 15.6.1.143+.
4
+ ---
5
+
6
+ # Supabase — Cron + Queues (background jobs)
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando implementar background jobs, scheduled tasks ou queues em Supabase **sem dependência externa** (Inngest, Trigger.dev, etc.). Trigger phrases:
11
+
12
+ - "pg_cron", "supabase cron job"
13
+ - "pgmq", "Postgres Message Queue"
14
+ - "pg_net", "HTTP from database"
15
+ - "background job Supabase"
16
+ - "scheduled task Supabase"
17
+
18
+ ## Regras absolutas
19
+
20
+ - **Extensions necessárias:**
21
+ - **`pg_cron`** — jobs scheduled (cron syntax)
22
+ - **`pgmq`** — Postgres Message Queue (requer Postgres **15.6.1.143+**)
23
+ - **`pg_net`** — HTTP requests do banco (recomendado v0.10.0+)
24
+ - **Pattern canônico:** **`cron → pgmq → Edge Function`** — `pg_cron` enfileira mensagens em `pgmq`, Edge Function consome (via cron ou polling).
25
+ - **Jobs `pg_cron` curtos** (< 10 min) — jobs longos bloqueiam scheduler. Para jobs longos, enfileire em `pgmq` e processe via Edge Function.
26
+ - **`pgmq.send`** para enfileirar; **`pgmq.read` + `pgmq.archive`** para consumir. Visibility timeout previne double processing.
27
+ - **`pg_net` é async** — `net.http_post` retorna `request_id`, response chega em `net._http_response`. Não bloqueia caller.
28
+ - **Idempotência** — Edge Function consumer deve ser idempotente (mesma mensagem pode ser entregue 2× em retry).
29
+ - **Cleanup** — sem `pgmq.archive` ou `pgmq.delete`, mensagem reaparece após visibility timeout (re-processed).
30
+
31
+ ## Patterns canônicos
32
+
33
+ ### Setup das extensions + criar fila
34
+
35
+ ```sql
36
+ -- PT-BR: habilitar extensions (uma vez por projeto)
37
+ create extension if not exists pg_cron;
38
+ create extension if not exists pgmq;
39
+ create extension if not exists pg_net;
40
+
41
+ -- PT-BR: criar fila pgmq
42
+ select pgmq.create('email_jobs');
43
+
44
+ -- PT-BR: opcional — criar fila com retention customizado
45
+ -- select pgmq.create_partitioned('large_jobs');
46
+ ```
47
+
48
+ ### Pattern canônico — `cron → pgmq → Edge Function`
49
+
50
+ ```sql
51
+ -- PT-BR: 1. cron job a cada 5 min enfileira pendências em pgmq
52
+ select cron.schedule(
53
+ 'enqueue-pending-emails',
54
+ '*/5 * * * *', -- a cada 5 min
55
+ $$
56
+ insert into pgmq.q_email_jobs (message)
57
+ select jsonb_build_object(
58
+ 'user_id', id,
59
+ 'kind', 'reminder',
60
+ 'enqueued_at', now()
61
+ )
62
+ from public.users
63
+ where pending_email = true
64
+ limit 1000; -- batch limitado
65
+ $$
66
+ );
67
+
68
+ -- PT-BR: 2. cron job a cada minuto despara processamento via Edge Function
69
+ select cron.schedule(
70
+ 'process-email-queue',
71
+ '*/1 * * * *', -- a cada minuto
72
+ $$
73
+ select net.http_post(
74
+ url := 'https://<project-ref>.supabase.co/functions/v1/process-emails',
75
+ headers := jsonb_build_object(
76
+ 'Content-Type', 'application/json',
77
+ 'Authorization', 'Bearer ' || current_setting('supabase.functions_token', true)
78
+ ),
79
+ body := '{}'::jsonb
80
+ );
81
+ $$
82
+ );
83
+ ```
84
+
85
+ ### Edge Function consumer — pgmq.read + archive
86
+
87
+ ```ts
88
+ // supabase/functions/process-emails/index.ts
89
+ // PT-BR: consume da fila pgmq, processa, archive
90
+ import { createClient } from 'npm:@supabase/supabase-js@2'
91
+
92
+ Deno.serve(async () => {
93
+ const supabase = createClient(
94
+ Deno.env.get('SUPABASE_URL')!,
95
+ Deno.env.get('SUPABASE_SECRET_KEYS')!
96
+ )
97
+
98
+ // PT-BR: pegar até 10 mensagens com visibility timeout 30s
99
+ const { data: msgs, error } = await supabase.rpc('pgmq_read', {
100
+ queue_name: 'email_jobs',
101
+ vt: 30, // visibility timeout em segundos
102
+ qty: 10, // máximo por chamada
103
+ })
104
+
105
+ if (error || !msgs?.length) {
106
+ return new Response('no jobs', { status: 200 })
107
+ }
108
+
109
+ for (const m of msgs) {
110
+ try {
111
+ // PT-BR: processar mensagem (idempotente!)
112
+ await sendEmail(m.message.user_id, m.message.kind)
113
+
114
+ // PT-BR: archive remove da fila e move para arquivo
115
+ await supabase.rpc('pgmq_archive', {
116
+ queue_name: 'email_jobs',
117
+ msg_id: m.msg_id,
118
+ })
119
+ } catch (err) {
120
+ // PT-BR: erro — não archive; visibility timeout expira e mensagem reaparece
121
+ console.error('processing error', m.msg_id, err)
122
+ }
123
+ }
124
+
125
+ return new Response(`processed ${msgs.length}`)
126
+ })
127
+ ```
128
+
129
+ ### Job cron simples — sem queue (cuidado: < 10 min)
130
+
131
+ ```sql
132
+ -- PT-BR: ok para tarefas leves e rápidas (cleanup, agregação)
133
+ select cron.schedule(
134
+ 'cleanup-old-sessions',
135
+ '0 3 * * *', -- 3am diário
136
+ $$
137
+ delete from public.sessions where expires_at < now() - interval '30 days';
138
+ $$
139
+ );
140
+ ```
141
+
142
+ ### Listar e remover jobs cron
143
+
144
+ ```sql
145
+ -- PT-BR: listar todos os jobs
146
+ select * from cron.job;
147
+
148
+ -- PT-BR: remover job
149
+ select cron.unschedule('process-email-queue');
150
+ ```
151
+
152
+ ### `pg_net.http_post` async
153
+
154
+ ```sql
155
+ -- PT-BR: dispara HTTP request, retorna request_id imediatamente
156
+ select net.http_post(
157
+ url := 'https://api.example.com/webhook',
158
+ headers := jsonb_build_object('Authorization', 'Bearer xxx'),
159
+ body := jsonb_build_object('event', 'task_completed'),
160
+ timeout_milliseconds := 5000
161
+ );
162
+
163
+ -- PT-BR: response chega em net._http_response (consultar depois)
164
+ select * from net._http_response order by created desc limit 10;
165
+ ```
166
+
167
+ ## Anti-patterns
168
+
169
+ ### Anti-pattern 1: Job cron longo (> 10 min)
170
+
171
+ **Errado:**
172
+ ```sql
173
+ select cron.schedule(
174
+ 'heavy-batch-process',
175
+ '0 * * * *',
176
+ $$ select pg_sleep(900); ... $$ -- ⚠ 15 min em pg_cron
177
+ );
178
+ ```
179
+
180
+ **Por quê:** `pg_cron` worker bloqueia outros jobs enquanto roda. Se job > 10 min ou trava, scheduler atrasa cascata. Em retry após failure, pode trancar inteiramente.
181
+
182
+ **Certo:** cron enfileira; Edge Function processa pesado:
183
+ ```sql
184
+ -- cron: leve (só enfileira)
185
+ select cron.schedule('enqueue-heavy', '0 * * * *', $$
186
+ insert into pgmq.q_heavy_jobs (message) select ...;
187
+ $$);
188
+ -- Edge Function: pesado (consome com timeout próprio)
189
+ ```
190
+
191
+ ### Anti-pattern 2: HTTP síncrono direto de pg_cron
192
+
193
+ **Errado:**
194
+ ```sql
195
+ select cron.schedule('call-api', '*/1 * * * *', $$
196
+ -- ⚠ pg_net é async, mas user pode tentar sync com loops
197
+ select net.http_get('https://api.example.com/long');
198
+ $$);
199
+ ```
200
+
201
+ **Por quê:** HTTP requests podem demorar segundos a minutos. Se response demora, próxima execução do cron empilha. Em alta latência, scheduler fica trancado.
202
+
203
+ **Certo:** enfileire em pgmq + Edge Function processa:
204
+ ```sql
205
+ -- cron: enfileira
206
+ insert into pgmq.q_api_calls (message) values ('{"endpoint": "/long"}');
207
+ -- Edge Function: chama API com timeout próprio + archive
208
+ ```
209
+
210
+ ### Anti-pattern 3: `pgmq.read` sem `archive` ou `delete`
211
+
212
+ **Errado:**
213
+ ```ts
214
+ const { data: msgs } = await supabase.rpc('pgmq_read', { queue_name: 'jobs', vt: 30, qty: 10 })
215
+ for (const m of msgs) {
216
+ await processJob(m.message)
217
+ // ⚠ esqueceu pgmq_archive
218
+ }
219
+ ```
220
+
221
+ **Por quê:** após visibility timeout (30s), mensagem reaparece — mesmo job rodado novamente. Em loop, leva a re-processing infinito.
222
+
223
+ **Certo:**
224
+ ```ts
225
+ for (const m of msgs) {
226
+ try {
227
+ await processJob(m.message)
228
+ await supabase.rpc('pgmq_archive', { queue_name: 'jobs', msg_id: m.msg_id })
229
+ } catch (err) {
230
+ // PT-BR: NÃO archive; mensagem retorna após vt para retry
231
+ }
232
+ }
233
+ ```
234
+
235
+ ### Anti-pattern 4: Edge Function não-idempotente
236
+
237
+ **Errado:**
238
+ ```ts
239
+ async function processJob(msg) {
240
+ await sendEmail(msg.user_id) // ⚠ envia email mesmo se já enviado
241
+ await chargeCard(msg.amount) // ⚠ cobra mesmo se já cobrado
242
+ }
243
+ ```
244
+
245
+ **Por quê:** retries entregam mesma mensagem 2×+. Sem idempotência, side effects duplicam — usuário recebe 2 emails ou é cobrado 2×.
246
+
247
+ **Certo:** rastreie estado:
248
+ ```ts
249
+ async function processJob(msg) {
250
+ const { data: existing } = await supabase
251
+ .from('email_log')
252
+ .select('id')
253
+ .eq('msg_id', msg.id)
254
+ .single()
255
+ if (existing) return // já processado
256
+ await sendEmail(msg.user_id)
257
+ await supabase.from('email_log').insert({ msg_id: msg.id })
258
+ }
259
+ ```
260
+
261
+ ## Padrões Exactly-Once em pgmq (v1.22+)
262
+
263
+ > Background jobs em pgmq tendem a duplicate processing em retry/timeout. Padrão canônico (DDIA Ch 11):
264
+ > 1. **Dedup table** com `unique(event_id)` — INSERT antes do processamento; falha = já processado.
265
+ > 2. **Idempotency key** no handler — mesmo input → mesmo output (sem efeitos colaterais).
266
+ > 3. **Transactional outbox** — write DB + event em mesma transação atomic; processador async lê outbox e publica.
267
+ >
268
+ > Detalhes completos em [`streams-eventos-cdc`](../streams-eventos-cdc/SKILL.md) (v1.22).
269
+
270
+ ## Ver também
271
+
272
+ - [supabase-edge-functions](../supabase-edge-functions/SKILL.md) — Edge Functions consumindo pgmq
273
+ - [supabase-database-functions](../supabase-database-functions/SKILL.md) — funções com `set search_path = ''` chamadas em cron
274
+ - [supabase-migrations](../supabase-migrations/SKILL.md) — extensions criadas em migrations
275
+ - [glossário](../_shared-supabase/glossary.md) — pg_cron, pgmq, pg_net