@luanpdd/kit-mcp 1.21.0 → 1.26.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 (275) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +914 -648
  3. package/kit/COMANDOS.md +138 -138
  4. package/kit/README.md +76 -52
  5. package/kit/agents/advisor-researcher.md +106 -106
  6. package/kit/agents/assumptions-analyzer.md +107 -107
  7. package/kit/agents/audit-log-implementer.md +138 -0
  8. package/kit/agents/auditor-consistencia-isolamento.md +413 -0
  9. package/kit/agents/codebase-mapper.md +768 -768
  10. package/kit/agents/crm-pipeline-implementer.md +106 -0
  11. package/kit/agents/debugger.md +813 -772
  12. package/kit/agents/detector-tenant-quente.md +337 -0
  13. package/kit/agents/evolution-go-integrator.md +21 -0
  14. package/kit/agents/example-reviewer.md +21 -21
  15. package/kit/agents/executor.md +564 -523
  16. package/kit/agents/integration-checker.md +200 -200
  17. package/kit/agents/invite-flow-implementer.md +52 -0
  18. package/kit/agents/lgpd-compliance-auditor.md +89 -0
  19. package/kit/agents/multi-tenant-isolation-auditor.md +10 -0
  20. package/kit/agents/multi-tenant-rls-writer.md +78 -0
  21. package/kit/agents/nyquist-auditor.md +178 -178
  22. package/kit/agents/org-onboarding-implementer.md +21 -0
  23. package/kit/agents/phase-researcher.md +696 -696
  24. package/kit/agents/plan-checker.md +272 -272
  25. package/kit/agents/planner.md +922 -891
  26. package/kit/agents/project-researcher.md +652 -652
  27. package/kit/agents/research-synthesizer.md +245 -245
  28. package/kit/agents/roadmapper.md +677 -677
  29. package/kit/agents/supabase-architect.md +27 -0
  30. package/kit/agents/supabase-auth-bootstrapper.md +80 -0
  31. package/kit/agents/supabase-column-privileges-writer.md +399 -0
  32. package/kit/agents/supabase-migration-writer.md +141 -14
  33. package/kit/agents/supabase-rbac-implementer.md +392 -0
  34. package/kit/agents/supabase-rls-hardener.md +521 -0
  35. package/kit/agents/supabase-rls-writer.md +105 -9
  36. package/kit/agents/supabase-roles-implementer.md +355 -0
  37. package/kit/agents/super-admin-implementer.md +99 -0
  38. package/kit/agents/ui-auditor.md +437 -437
  39. package/kit/agents/ui-checker.md +302 -302
  40. package/kit/agents/ui-researcher.md +355 -355
  41. package/kit/agents/user-profiler.md +175 -175
  42. package/kit/agents/validador-evolucao-schema.md +335 -0
  43. package/kit/agents/verifier.md +728 -728
  44. package/kit/commands/adicionar-backlog.md +75 -75
  45. package/kit/commands/adicionar-fase.md +42 -42
  46. package/kit/commands/adicionar-tarefa.md +45 -45
  47. package/kit/commands/adicionar-testes.md +41 -41
  48. package/kit/commands/ajuda.md +21 -21
  49. package/kit/commands/atualizar.md +37 -37
  50. package/kit/commands/auditar-marco.md +179 -179
  51. package/kit/commands/auditar-uat.md +23 -23
  52. package/kit/commands/autonomo.md +40 -40
  53. package/kit/commands/branch-pr.md +24 -24
  54. package/kit/commands/concluir-marco.md +247 -247
  55. package/kit/commands/configuracoes.md +36 -36
  56. package/kit/commands/dados-distribuidos.md +188 -0
  57. package/kit/commands/definir-perfil.md +10 -10
  58. package/kit/commands/depurar.md +190 -190
  59. package/kit/commands/discutir-fase.md +131 -131
  60. package/kit/commands/entrar-discord.md +17 -17
  61. package/kit/commands/estatisticas.md +18 -18
  62. package/kit/commands/example-greeting.md +33 -33
  63. package/kit/commands/executar-fase.md +58 -58
  64. package/kit/commands/expresso.md +56 -56
  65. package/kit/commands/fase-ui.md +34 -34
  66. package/kit/commands/fazer.md +57 -57
  67. package/kit/commands/fio.md +125 -125
  68. package/kit/commands/fluxos-trabalho.md +64 -64
  69. package/kit/commands/forense.md +176 -176
  70. package/kit/commands/gerenciador.md +38 -38
  71. package/kit/commands/inserir-fase.md +31 -31
  72. package/kit/commands/limpeza.md +17 -17
  73. package/kit/commands/listar-hipoteses-fase.md +45 -45
  74. package/kit/commands/listar-workspaces.md +18 -18
  75. package/kit/commands/mapear-codebase.md +70 -70
  76. package/kit/commands/nota.md +33 -33
  77. package/kit/commands/novo-marco.md +43 -43
  78. package/kit/commands/novo-projeto.md +41 -41
  79. package/kit/commands/novo-workspace.md +43 -43
  80. package/kit/commands/pausar-trabalho.md +37 -37
  81. package/kit/commands/perfil-usuario.md +45 -45
  82. package/kit/commands/pesquisar-fase.md +195 -195
  83. package/kit/commands/planejar-fase.md +67 -67
  84. package/kit/commands/planejar-lacunas.md +33 -33
  85. package/kit/commands/plantar-ideia.md +25 -25
  86. package/kit/commands/progresso.md +24 -24
  87. package/kit/commands/proximo.md +30 -30
  88. package/kit/commands/publicar.md +490 -490
  89. package/kit/commands/rapido.md +35 -35
  90. package/kit/commands/reaplicar-patches.md +124 -124
  91. package/kit/commands/relatorio-sessao.md +19 -19
  92. package/kit/commands/remover-fase.md +31 -31
  93. package/kit/commands/remover-workspace.md +26 -26
  94. package/kit/commands/resumo-marco.md +50 -50
  95. package/kit/commands/retomar-trabalho.md +40 -40
  96. package/kit/commands/revisar-backlog.md +60 -60
  97. package/kit/commands/revisar-ui.md +32 -32
  98. package/kit/commands/revisar.md +37 -37
  99. package/kit/commands/saude.md +21 -21
  100. package/kit/commands/setup-notion.md +93 -93
  101. package/kit/commands/supabase.md +55 -8
  102. package/kit/commands/sync-main.md +68 -68
  103. package/kit/commands/validar-fase.md +35 -35
  104. package/kit/commands/verificar-tarefas.md +44 -44
  105. package/kit/commands/verificar-trabalho.md +64 -64
  106. package/kit/file-manifest.json +52 -32
  107. package/kit/framework/bin/lib/commands.cjs +959 -959
  108. package/kit/framework/bin/lib/config.cjs +442 -442
  109. package/kit/framework/bin/lib/core.cjs +1230 -1230
  110. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  111. package/kit/framework/bin/lib/init.cjs +1442 -1442
  112. package/kit/framework/bin/lib/milestone.cjs +252 -252
  113. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  114. package/kit/framework/bin/lib/phase.cjs +888 -888
  115. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  116. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  117. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  118. package/kit/framework/bin/lib/security.cjs +382 -382
  119. package/kit/framework/bin/lib/state.cjs +1031 -1031
  120. package/kit/framework/bin/lib/template.cjs +222 -222
  121. package/kit/framework/bin/lib/uat.cjs +282 -282
  122. package/kit/framework/bin/lib/verify.cjs +888 -888
  123. package/kit/framework/bin/lib/workstream.cjs +491 -491
  124. package/kit/framework/bin/tools.cjs +918 -918
  125. package/kit/framework/commands/workstreams.md +63 -63
  126. package/kit/framework/references/checkpoints.md +778 -778
  127. package/kit/framework/references/continuation-format.md +249 -249
  128. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  129. package/kit/framework/references/git-integration.md +295 -295
  130. package/kit/framework/references/git-planning-commit.md +38 -38
  131. package/kit/framework/references/model-profile-resolution.md +36 -36
  132. package/kit/framework/references/model-profiles.md +139 -139
  133. package/kit/framework/references/phase-argument-parsing.md +61 -61
  134. package/kit/framework/references/planning-config.md +202 -202
  135. package/kit/framework/references/questioning.md +162 -162
  136. package/kit/framework/references/tdd.md +263 -263
  137. package/kit/framework/references/ui-brand.md +160 -160
  138. package/kit/framework/references/user-profiling.md +657 -657
  139. package/kit/framework/references/verification-patterns.md +612 -612
  140. package/kit/framework/references/workstream-flag.md +58 -58
  141. package/kit/framework/templates/DEBUG.md +164 -164
  142. package/kit/framework/templates/UAT.md +265 -265
  143. package/kit/framework/templates/UI-SPEC.md +100 -100
  144. package/kit/framework/templates/VALIDATION.md +76 -76
  145. package/kit/framework/templates/claude-md.md +122 -122
  146. package/kit/framework/templates/codebase/architecture.md +185 -185
  147. package/kit/framework/templates/codebase/concerns.md +205 -205
  148. package/kit/framework/templates/codebase/conventions.md +204 -204
  149. package/kit/framework/templates/codebase/integrations.md +192 -192
  150. package/kit/framework/templates/codebase/stack.md +158 -158
  151. package/kit/framework/templates/codebase/structure.md +199 -199
  152. package/kit/framework/templates/codebase/testing.md +301 -301
  153. package/kit/framework/templates/config.json +44 -44
  154. package/kit/framework/templates/context.md +352 -352
  155. package/kit/framework/templates/continue-here.md +78 -78
  156. package/kit/framework/templates/copilot-instructions.md +7 -7
  157. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  158. package/kit/framework/templates/dev-preferences.md +20 -20
  159. package/kit/framework/templates/discovery.md +146 -146
  160. package/kit/framework/templates/discussion-log.md +63 -63
  161. package/kit/framework/templates/milestone-archive.md +123 -123
  162. package/kit/framework/templates/milestone.md +115 -115
  163. package/kit/framework/templates/phase-prompt.md +610 -610
  164. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  165. package/kit/framework/templates/project.md +186 -186
  166. package/kit/framework/templates/requirements.md +231 -231
  167. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  168. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  169. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  170. package/kit/framework/templates/research-project/STACK.md +120 -120
  171. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  172. package/kit/framework/templates/research.md +419 -419
  173. package/kit/framework/templates/retrospective.md +54 -54
  174. package/kit/framework/templates/roadmap.md +202 -202
  175. package/kit/framework/templates/state.md +176 -176
  176. package/kit/framework/templates/summary-complex.md +59 -59
  177. package/kit/framework/templates/summary-minimal.md +41 -41
  178. package/kit/framework/templates/summary-standard.md +48 -48
  179. package/kit/framework/templates/summary.md +209 -209
  180. package/kit/framework/templates/user-profile.md +146 -146
  181. package/kit/framework/templates/user-setup.md +256 -256
  182. package/kit/framework/templates/verification-report.md +258 -258
  183. package/kit/framework/workflows/add-phase.md +112 -112
  184. package/kit/framework/workflows/add-tests.md +351 -351
  185. package/kit/framework/workflows/add-todo.md +158 -158
  186. package/kit/framework/workflows/audit-milestone.md +340 -340
  187. package/kit/framework/workflows/audit-uat.md +109 -109
  188. package/kit/framework/workflows/autonomous.md +891 -891
  189. package/kit/framework/workflows/check-todos.md +177 -177
  190. package/kit/framework/workflows/cleanup.md +152 -152
  191. package/kit/framework/workflows/complete-milestone.md +696 -696
  192. package/kit/framework/workflows/diagnose-issues.md +231 -231
  193. package/kit/framework/workflows/discovery-phase.md +289 -289
  194. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  195. package/kit/framework/workflows/discuss-phase.md +784 -784
  196. package/kit/framework/workflows/do.md +104 -104
  197. package/kit/framework/workflows/execute-phase.md +838 -838
  198. package/kit/framework/workflows/execute-plan.md +510 -510
  199. package/kit/framework/workflows/fast.md +102 -102
  200. package/kit/framework/workflows/forensics.md +265 -265
  201. package/kit/framework/workflows/health.md +181 -181
  202. package/kit/framework/workflows/help.md +619 -619
  203. package/kit/framework/workflows/insert-phase.md +130 -130
  204. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  205. package/kit/framework/workflows/list-workspaces.md +56 -56
  206. package/kit/framework/workflows/manager.md +362 -362
  207. package/kit/framework/workflows/map-codebase.md +377 -377
  208. package/kit/framework/workflows/milestone-summary.md +223 -223
  209. package/kit/framework/workflows/new-milestone.md +486 -486
  210. package/kit/framework/workflows/new-project.md +1159 -1159
  211. package/kit/framework/workflows/new-workspace.md +237 -237
  212. package/kit/framework/workflows/next.md +97 -97
  213. package/kit/framework/workflows/node-repair.md +92 -92
  214. package/kit/framework/workflows/note.md +156 -156
  215. package/kit/framework/workflows/pause-work.md +176 -176
  216. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  217. package/kit/framework/workflows/plan-phase.md +765 -765
  218. package/kit/framework/workflows/plant-seed.md +169 -169
  219. package/kit/framework/workflows/pr-branch.md +129 -129
  220. package/kit/framework/workflows/profile-user.md +450 -450
  221. package/kit/framework/workflows/progress.md +507 -507
  222. package/kit/framework/workflows/quick.md +757 -757
  223. package/kit/framework/workflows/remove-phase.md +155 -155
  224. package/kit/framework/workflows/remove-workspace.md +90 -90
  225. package/kit/framework/workflows/research-phase.md +82 -82
  226. package/kit/framework/workflows/resume-project.md +326 -326
  227. package/kit/framework/workflows/review.md +228 -228
  228. package/kit/framework/workflows/session-report.md +146 -146
  229. package/kit/framework/workflows/settings.md +283 -283
  230. package/kit/framework/workflows/ship.md +228 -228
  231. package/kit/framework/workflows/stats.md +60 -60
  232. package/kit/framework/workflows/transition.md +671 -671
  233. package/kit/framework/workflows/ui-phase.md +302 -302
  234. package/kit/framework/workflows/ui-review.md +165 -165
  235. package/kit/framework/workflows/update.md +323 -323
  236. package/kit/framework/workflows/validate-phase.md +174 -174
  237. package/kit/framework/workflows/verify-phase.md +252 -252
  238. package/kit/framework/workflows/verify-work.md +637 -637
  239. package/kit/hooks/check-update.js +118 -118
  240. package/kit/hooks/context-monitor.js +163 -163
  241. package/kit/hooks/prompt-guard.js +103 -103
  242. package/kit/hooks/statusline.js +125 -125
  243. package/kit/hooks/workflow-guard.js +101 -101
  244. package/kit/settings.json +45 -45
  245. package/kit/skills/_shared-dados-distribuidos/glossary.md +224 -0
  246. package/kit/skills/_shared-supabase/glossary.md +27 -0
  247. package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -0
  248. package/kit/skills/audit-log-multi-tenant/SKILL.md +6 -0
  249. package/kit/skills/cascading-failures/SKILL.md +4 -0
  250. package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -0
  251. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +17 -0
  252. package/kit/skills/escolha-modelo-consistencia/SKILL.md +495 -0
  253. package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -0
  254. package/kit/skills/example-skill/SKILL.md +42 -42
  255. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +4 -0
  256. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +4 -0
  257. package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -0
  258. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +37 -0
  259. package/kit/skills/streams-eventos-cdc/SKILL.md +712 -0
  260. package/kit/skills/supabase-column-level-security/SKILL.md +426 -0
  261. package/kit/skills/supabase-cron-queues/SKILL.md +9 -0
  262. package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -0
  263. package/kit/skills/supabase-database-functions/SKILL.md +85 -0
  264. package/kit/skills/supabase-migrations/SKILL.md +133 -11
  265. package/kit/skills/supabase-postgres-roles/SKILL.md +392 -0
  266. package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -0
  267. package/kit/skills/supabase-rls-policies/SKILL.md +462 -12
  268. package/kit/skills/super-admin-platform-pattern/SKILL.md +4 -0
  269. package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -0
  270. package/package.json +63 -63
  271. package/src/core/kit.js +216 -216
  272. package/src/core/reflect.js +247 -247
  273. package/src/core/reverse-sync.js +372 -372
  274. package/src/core/sync.js +418 -418
  275. package/src/core/watch.js +121 -121
@@ -0,0 +1,385 @@
1
+ ---
2
+ name: consistencia-leitura-replica
3
+ description: Use ao usar Supabase read replicas via Supavisor (porta 6543) ou ao combinar Realtime broadcast + leitura DB — 3 problemas canônicos DDIA Ch 5 (read-after-write inconsistente, leituras não-monotônicas, prefixo causal violado), 3 soluções para Supabase (leitura no líder após escrita, sticky session por user_id, detecção stale via pg_last_wal_replay_lsn), padrão "ler o próprio broadcast" para evitar re-fetch após broadcast.
4
+ ---
5
+
6
+ # Consistência Leitura Réplica — Supabase + Supavisor + Realtime
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill ao usar Supabase Pro+ com **read replicas** ou ao combinar Realtime broadcast com leitura subsequente do DB. Trigger phrases:
11
+
12
+ - "Supabase read replica", "réplica de leitura"
13
+ - "porta 6543 vs 5432", "Supavisor session vs transaction"
14
+ - "read-after-write", "leitura após escrita inconsistente"
15
+ - "monotonic reads", "leituras não-monotônicas"
16
+ - "consistent prefix reads", "prefixo causal violado"
17
+ - "replication lag Supabase", "atraso de replicação"
18
+ - "broadcast Realtime + SELECT stale"
19
+ - "pg_last_wal_replay_lsn", "WAL position detection"
20
+
21
+ Esta skill aplica **DDIA Ch 5 "Problems With Replication Lag"** ao stack Supabase. Cross-referenciada por `supabase-realtime` (v1.8) ao bundlear broadcast + leitura, e por `multi-tenant-performance-scaling` (v1.21) ao escalar Postgres em Pro+.
22
+
23
+ Termos canônicos (`read-after-write consistency`, `monotonic reads`, `consistent prefix reads`, `replication lag`, `leader-follower replication`) definidos em [`_shared-dados-distribuidos/glossary.md`](../_shared-dados-distribuidos/glossary.md) seções (a) e (b) — esta skill **não duplica**, apenas linka.
24
+
25
+ ## Regras absolutas
26
+
27
+ **REGRA #1 (read-after-write own data):** Para leituras do **próprio dado do usuário** dentro de janela de **5s após write**, rotear para **porta 5432 (líder)** — não 6543. Sem isso, usuário cria post → GET retorna 404 → percepção de bug. Justify: DDIA p. 156 "always read the user's own profile from the leader".
28
+
29
+ **REGRA #2 (sticky session monotonic):** Para usuários ativos lendo de réplicas, escolher réplica via `hash(user_id) mod N` — **não round-robin**. Round-robin viola monotonic reads (DDIA p. 158). Mitigação obrigatória: fallback para líder se réplica down.
30
+
31
+ **REGRA #3 (broadcast trust payload):** Após receber Realtime broadcast com `payload.record`, **NÃO** fazer SELECT subsequente. Confiar no payload — server é a fonte canônica do evento. SELECT pode atingir réplica que ainda não replicou (lag típico 50-500ms).
32
+
33
+ **REGRA #4 (causal partition):** Writes causalmente relacionados (pergunta + resposta em chat, parent + child em árvore) **DEVEM** ir para a mesma partição lógica. Em Supabase: usar mesmo `org_id` ou `conversation_id` como partition key. DDIA p. 159 "any writes which are causally related to each other are written to the same partition".
34
+
35
+ **REGRA #5 (LSN wait com timeout):** Quando usar `pg_last_wal_replay_lsn() >= captured_lsn`, **SEMPRE** com timeout (3-5s). Sem timeout, query trava se réplica falhou. Após timeout, fallback para líder.
36
+
37
+ ## Patterns canônicos
38
+
39
+ ### Problema 1: Read-after-write inconsistente (DDIA Ch 5, p. 156)
40
+
41
+ **Cenário canônico:** usuário cria post via form submit → tela mostra "criado com sucesso" → usuário clica "ver post" → request vai para réplica → réplica ainda não replicou → 404. Da perspectiva do usuário: "perdi meu dado".
42
+
43
+ ```
44
+ User 1234 ──INSERT─→ Leader (5432) ┐
45
+ │ │ replication lag (50-500ms)
46
+ └──WAL──────────────→ Follower (6543, replica)
47
+
48
+ User 1234 ─SELECT────────────────────────────→ ❌ 404
49
+ (vai para replica via Supavisor)
50
+ ```
51
+
52
+ **Solução A — leitura no líder após write do mesmo usuário:**
53
+
54
+ ```typescript
55
+ // PT-BR: client mantém timestamp do último write em memória
56
+ class SupabaseRouter {
57
+ private lastWriteAt: Map<string, number> = new Map() // userId → timestamp ms
58
+ private readonly STICKY_WINDOW_MS = 5000 // 5s leitura no líder
59
+
60
+ async write(userId: string, table: string, payload: unknown) {
61
+ const result = await this.leaderClient.from(table).insert(payload)
62
+ this.lastWriteAt.set(userId, Date.now())
63
+ return result
64
+ }
65
+
66
+ async read(userId: string, table: string, filter: object) {
67
+ const lastWrite = this.lastWriteAt.get(userId) ?? 0
68
+ const elapsedMs = Date.now() - lastWrite
69
+
70
+ // PT-BR: dentro da janela 5s, ler do líder (porta 5432)
71
+ if (elapsedMs < this.STICKY_WINDOW_MS) {
72
+ return this.leaderClient.from(table).select().match(filter)
73
+ }
74
+ // PT-BR: fora da janela, OK ler de replica via pooler 6543
75
+ return this.replicaClient.from(table).select().match(filter)
76
+ }
77
+ }
78
+ ```
79
+
80
+ **Trade-off:** dentro da janela, perde benefício do read scaling. DDIA recomenda janela curta (1-5s) — cobre 99% dos casos UX sem sobrecarregar líder.
81
+
82
+ ### Problema 2: Leituras não-monotônicas (DDIA Ch 5, p. 158)
83
+
84
+ **Cenário canônico:** usuário 2345 abre lista de comentários — primeira leitura vai para réplica 1 (lag 100ms) → vê comentário X. Segundo refresh vai para réplica 2 (lag 800ms) → comentário X **desapareceu**. Da perspectiva do usuário: "dado voltou no tempo".
85
+
86
+ ```
87
+ User 2345 ─SELECT(1)────→ Replica 1 (lag 100ms) → 1 result ✅
88
+ User 2345 ─SELECT(2)────→ Replica 2 (lag 800ms) → 0 results ❌ (parecia ter sumido)
89
+ ```
90
+
91
+ **Solução B — sticky session por `user_id` em routing:**
92
+
93
+ ```typescript
94
+ // PT-BR: hash determinístico do user_id → escolhe replica fixa para esse user
95
+ import { createHash } from 'node:crypto'
96
+
97
+ function pickReplica(userId: string, replicas: ReadonlyArray<SupabaseClient>): SupabaseClient {
98
+ const hash = createHash('sha256').update(userId).digest()
99
+ const idx = hash.readUInt32BE(0) % replicas.length
100
+ return replicas[idx]
101
+ }
102
+
103
+ // PT-BR: usage com fallback para líder se replica falhar
104
+ async function readWithStickyReplica(userId: string, query: () => Promise<Result>) {
105
+ const replica = pickReplica(userId, REPLICAS)
106
+ try {
107
+ return await query.call(replica)
108
+ } catch (err) {
109
+ if (isReplicaDownError(err)) {
110
+ // PT-BR: fallback obrigatório — REGRA #2
111
+ return await query.call(LEADER)
112
+ }
113
+ throw err
114
+ }
115
+ }
116
+ ```
117
+
118
+ **Pitfall:** réplica fica down → todos os usuários alocados a ela ficam sem leitura. Mitigação: detectar via health check + reroute para líder até réplica voltar.
119
+
120
+ ### Problema 3: Prefixo causal violado (DDIA Ch 5, p. 159)
121
+
122
+ **Cenário canônico chat:** Mr Poons pergunta "How far into the future can you see, Mrs Cake?" → write vai para partição A. Mrs Cake responde "About ten seconds usually, Mr Poons." → write vai para partição B. Observador lê de B (lag baixo) e A (lag alto): **vê resposta antes da pergunta**.
123
+
124
+ ```
125
+ Partição A (lag 800ms): "How far into the future..."
126
+ Partição B (lag 100ms): "About ten seconds..."
127
+
128
+ Observer lê B → vê resposta ✅
129
+ Observer lê A → vê pergunta ✅
130
+ Observer ordem percebida: resposta → pergunta ❌
131
+ ```
132
+
133
+ **Solução parcial — particionamento por chave causal:**
134
+
135
+ ```sql
136
+ -- PT-BR: ambas msgs (pergunta + resposta) particionadas por conversation_id
137
+ -- garante mesma partição lógica = mesma ordem WAL
138
+ create table public.messages (
139
+ id uuid primary key default gen_random_uuid(),
140
+ conversation_id uuid not null,
141
+ author_id uuid not null,
142
+ body text not null,
143
+ created_at timestamptz not null default now()
144
+ ) partition by hash (conversation_id);
145
+
146
+ -- PT-BR: indexes ajudam ordering
147
+ create index messages_conv_created_idx
148
+ on public.messages (conversation_id, created_at);
149
+ ```
150
+
151
+ **Limitação:** garante consistent prefix dentro de uma partição. Cross-partition (ex: usuário em duas conversações simultâneas), DDIA p. 159 conclui "in general, ensuring consistent prefix reads requires snapshot isolation". Em Supabase prático: **manter conversação em uma tabela com chave causal explícita**.
152
+
153
+ ### Solução C — Detecção stale via `pg_last_wal_replay_lsn()`
154
+
155
+ Quando precisa de garantia "este read viu meu write" sem rotear ao líder:
156
+
157
+ ```sql
158
+ -- PT-BR: capturar LSN no líder após write (chamada do app via RPC)
159
+ create or replace function public.get_current_lsn()
160
+ returns text
161
+ language sql
162
+ security invoker
163
+ set search_path = ''
164
+ as $$
165
+ -- PT-BR: pg_current_wal_lsn() retorna posição atual do WAL no líder
166
+ select pg_current_wal_lsn()::text;
167
+ $$;
168
+
169
+ -- PT-BR: na replica, esperar até replay alcançar o LSN capturado
170
+ create or replace function public.wait_for_lsn(target_lsn text, timeout_ms int default 5000)
171
+ returns boolean
172
+ language plpgsql
173
+ security invoker
174
+ set search_path = ''
175
+ as $$
176
+ declare
177
+ start_at timestamptz := clock_timestamp();
178
+ elapsed_ms int;
179
+ begin
180
+ loop
181
+ -- PT-BR: pg_last_wal_replay_lsn() na replica = última posição replayed
182
+ if pg_last_wal_replay_lsn() >= target_lsn::pg_lsn then
183
+ return true;
184
+ end if;
185
+
186
+ elapsed_ms := extract(milliseconds from (clock_timestamp() - start_at))::int;
187
+ if elapsed_ms >= timeout_ms then
188
+ return false; -- PT-BR: REGRA #5 — timeout sem bloquear infinito
189
+ end if;
190
+
191
+ perform pg_sleep(0.05); -- PT-BR: 50ms entre polls
192
+ end loop;
193
+ end;
194
+ $$;
195
+ ```
196
+
197
+ **Uso típico no client:**
198
+
199
+ ```typescript
200
+ // PT-BR: 1) write no líder, captura LSN
201
+ const { data: lsn } = await leaderClient.rpc('get_current_lsn')
202
+ await leaderClient.from('orders').insert(order)
203
+
204
+ // PT-BR: 2) read no líder, espera replay
205
+ const { data: ready } = await replicaClient.rpc('wait_for_lsn', {
206
+ target_lsn: lsn,
207
+ timeout_ms: 3000,
208
+ })
209
+
210
+ if (ready) {
211
+ // PT-BR: replica caught up, leitura é safe
212
+ return replicaClient.from('orders').select().eq('id', order.id)
213
+ }
214
+ // PT-BR: timeout — fallback para líder (REGRA #5)
215
+ return leaderClient.from('orders').select().eq('id', order.id)
216
+ ```
217
+
218
+ ### Supavisor read replica routing
219
+
220
+ | Porta | Modo | Use case | Connection string |
221
+ |---|---|---|---|
222
+ | **6543** | Transaction (default Pro+) | Apps com pooler já configurado, edge runtimes, serverless | `postgresql://postgres.[ref]:pwd@aws-0-region.pooler.supabase.com:6543/postgres` |
223
+ | **5432** | Session (líder) | Reads críticas (read-after-write), writes, prepared statements, advisory locks | `postgresql://postgres.[ref]:pwd@aws-0-region.pooler.supabase.com:5432/postgres` |
224
+ | `pooler.read.*` | Réplica routing | Read-heavy workloads em Pro+ com replicas habilitadas | (futuro Supabase feature — placeholder hoje) |
225
+
226
+ **Decisão por tipo de query:**
227
+
228
+ ```
229
+ SELECT do próprio dado dentro 5s do write? → 5432 (líder, REGRA #1)
230
+ SELECT cross-user, sem janela sticky? → 6543 (replica via Supavisor)
231
+ INSERT / UPDATE / DELETE? → 5432 (sempre líder)
232
+ SELECT FOR UPDATE / advisory lock? → 5432 (transaction precisa session mode)
233
+ ```
234
+
235
+ Cross-ref ATIVO: [`multi-tenant-performance-scaling/SKILL.md`](../multi-tenant-performance-scaling/SKILL.md) (v1.21) cobre Supavisor REGRA #1 sob lente de connection pooling — esta skill cobre a mesma porta sob lente de consistência.
236
+
237
+ ### Realtime broadcast + leitura DB — padrão "ler o próprio broadcast"
238
+
239
+ **Cenário canônico:** client A faz INSERT em `orders` → server emite Realtime broadcast `new_order` no canal `org:orders:org_42` → client B recebe broadcast → client B faz SELECT para refresh da lista. **Pode receber dado stale** (replica não replicou ainda).
240
+
241
+ **Sequência do bug:**
242
+
243
+ ```
244
+ t=0ms Client A INSERT → Leader
245
+ t=10ms Server emite broadcast → todos clients no canal recebem
246
+ t=15ms Client B recebe broadcast → triggers re-fetch
247
+ t=20ms Client B SELECT → Replica (lag ainda 80ms)
248
+ t=20ms Replica retorna lista SEM o novo order ❌
249
+ t=80ms Replica finalmente replicou (mas client B já desenhou stale)
250
+ ```
251
+
252
+ **Padrão correto — confiar no payload broadcast:**
253
+
254
+ ```typescript
255
+ // PT-BR: client confia no payload, NÃO faz SELECT subsequente
256
+ import { createClient } from '@supabase/supabase-js'
257
+
258
+ const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
259
+
260
+ const channel = supabase
261
+ .channel('org:orders:org_42', { config: { private: true } })
262
+ .on('broadcast', { event: 'new_order' }, ({ payload }) => {
263
+ // PT-BR: REGRA #3 — confie no payload, NÃO faça SELECT
264
+ setOrders((prev) => [...prev, payload.record])
265
+ })
266
+ .subscribe()
267
+
268
+ // PT-BR: cleanup obrigatório — pattern de supabase-realtime v1.8
269
+ return () => {
270
+ supabase.removeChannel(channel)
271
+ }
272
+ ```
273
+
274
+ **Server side — emitir payload completo no broadcast:**
275
+
276
+ ```typescript
277
+ // PT-BR: Edge Function que cria order e broadcast com record completo
278
+ Deno.serve(async (req) => {
279
+ const supabase = createClient(SUPABASE_URL, SERVICE_ROLE_KEY)
280
+ const order = await req.json()
281
+
282
+ // PT-BR: 1) INSERT no líder
283
+ const { data: created } = await supabase
284
+ .from('orders')
285
+ .insert(order)
286
+ .select()
287
+ .single()
288
+
289
+ // PT-BR: 2) broadcast com record completo — clients confiam neste payload
290
+ await supabase
291
+ .channel(`org:orders:${order.org_id}`)
292
+ .send({
293
+ type: 'broadcast',
294
+ event: 'new_order',
295
+ payload: { record: created }, // PT-BR: payload canônico
296
+ })
297
+
298
+ return new Response(JSON.stringify(created), { status: 201 })
299
+ })
300
+ ```
301
+
302
+ **Cross-ref ATIVO:** [`supabase-realtime/SKILL.md`](../supabase-realtime/SKILL.md) (v1.8) define padrão de canal (`scope:entity:id`, `private:true`, `removeChannel` cleanup). Esta skill estende com o padrão `payload.record` específico para evitar replica lag bug.
303
+
304
+ ## Anti-patterns
305
+
306
+ ### Anti-pattern 1: Round-robin entre réplicas para o mesmo usuário
307
+
308
+ **Errado:**
309
+ ```typescript
310
+ // PT-BR: pegar replica aleatoriamente cada read
311
+ const replica = REPLICAS[Math.floor(Math.random() * REPLICAS.length)]
312
+ return replica.from('messages').select()
313
+ ```
314
+
315
+ **Por quê:** viola monotonic reads (DDIA p. 158). User vê mensagem X em uma leitura, depois X some na próxima (replica 2 ainda não replicou). Leituras "voltam no tempo".
316
+
317
+ **Certo:** sticky session por `hash(user_id) mod N` (Solução B acima).
318
+
319
+ ### Anti-pattern 2: Re-fetch após broadcast
320
+
321
+ **Errado:**
322
+ ```typescript
323
+ .on('broadcast', { event: 'new_order' }, async () => {
324
+ // PT-BR: re-fetch que pode atingir replica stale
325
+ const { data } = await supabase.from('orders').select() // ❌
326
+ setOrders(data)
327
+ })
328
+ ```
329
+
330
+ **Por quê:** broadcast chega em 10-15ms, mas replication lag tipicamente 50-500ms. SELECT no callback **garantido** vai chegar antes da replica replicar. Bug "intermittent missing data".
331
+
332
+ **Certo:** confiar no `payload.record` enviado pelo server (REGRA #3 + Solução padrão "ler o próprio broadcast").
333
+
334
+ ### Anti-pattern 3: `pg_last_wal_replay_lsn()` sem timeout
335
+
336
+ **Errado:**
337
+ ```sql
338
+ -- PT-BR: loop infinito se replica falhou
339
+ do $$
340
+ begin
341
+ loop
342
+ if pg_last_wal_replay_lsn() >= captured_lsn::pg_lsn then exit; end if;
343
+ perform pg_sleep(0.05);
344
+ end loop;
345
+ end$$;
346
+ ```
347
+
348
+ **Por quê:** se replica desconectou do WAL stream (network partition, disk full), `pg_last_wal_replay_lsn()` nunca alcança o LSN do líder. Query trava indefinidamente, esgota connection pool.
349
+
350
+ **Certo:** timeout 3-5s + fallback explícito para líder (REGRA #5 + função `wait_for_lsn` acima).
351
+
352
+ ### Anti-pattern 4: Cross-partition para conversação causal
353
+
354
+ **Errado:**
355
+ ```sql
356
+ -- PT-BR: messages particionadas por created_at (range temporal)
357
+ create table public.messages (...) partition by range (created_at);
358
+ ```
359
+
360
+ **Por quê:** pergunta e resposta em uma conversação podem cair em partições diferentes (se virada de mês entre as duas). Viola consistent prefix reads — observador vê resposta antes da pergunta.
361
+
362
+ **Certo:** particionar por `conversation_id` (HASH), garante que toda a conversação fica na mesma partição = mesma ordem WAL = consistent prefix.
363
+
364
+ ### Anti-pattern 5: Porta 6543 para `SELECT FOR UPDATE`
365
+
366
+ **Errado:**
367
+ ```typescript
368
+ // PT-BR: tentando lock pessimista via Supavisor transaction mode
369
+ const { data } = await client6543.rpc('lock_order', { id })
370
+ ```
371
+
372
+ **Por quê:** Supavisor 6543 (transaction mode) não preserva sessão entre statements — `SELECT FOR UPDATE` libera o lock na próxima query. Lock vira no-op silencioso.
373
+
374
+ **Certo:** porta 5432 (session mode) para qualquer operação que precisa estado de sessão (locks, prepared statements, `SET LOCAL`, advisory locks).
375
+
376
+ ## Ver também
377
+
378
+ - [`_shared-dados-distribuidos/glossary.md`](../_shared-dados-distribuidos/glossary.md) — termos canônicos `read-after-write consistency`, `monotonic reads`, `consistent prefix reads`, `replication lag`, `leader-follower replication` (Phase 117)
379
+ - [`supabase-realtime/SKILL.md`](../supabase-realtime/SKILL.md) — broadcast com `private:true`, naming `scope:entity:id`, cleanup `removeChannel` (v1.8)
380
+ - [`multi-tenant-performance-scaling/SKILL.md`](../multi-tenant-performance-scaling/SKILL.md) — Supavisor connection string canônica, REGRA #1 porta 6543 (v1.21)
381
+ - [`supabase-database-functions/SKILL.md`](../supabase-database-functions/SKILL.md) — padrões PG functions (security invoker, search_path) usados em `get_current_lsn` e `wait_for_lsn`
382
+ - [Designing Data-Intensive Applications, Martin Kleppmann (O'Reilly 2017)](https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/) — Ch 5 "Problems With Replication Lag" (p. 155-160)
383
+ - [PostgreSQL Documentation — pg_last_wal_replay_lsn](https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-RECOVERY-INFO)
384
+ - [Supabase Read Replicas](https://supabase.com/docs/guides/platform/read-replicas)
385
+ - [Supabase Supavisor 1M Connections](https://supabase.com/blog/supavisor-1-million)
@@ -316,6 +316,23 @@ delete from public.leads where id = '<lead_id>';
316
316
 
317
317
  **Certo:** soft delete (`status = 'archived'`) ou FK CASCADE com cuidado + audit log antes.
318
318
 
319
+ ## Prevenção de Lost Update em Stage Transition (v1.22+)
320
+
321
+ > A trigger `validate_lead_stage_transition` deve usar `SELECT ... FOR UPDATE` em rows lidas para prevenir lost update quando 2 reps tentam mover o mesmo lead simultaneamente. Padrão completo em [`postgres-isolamento-concorrencia`](../postgres-isolamento-concorrencia/SKILL.md) (v1.22 — DDIA Ch 7).
322
+
323
+ Exemplo aplicado:
324
+
325
+ ```sql
326
+ CREATE OR REPLACE FUNCTION validate_lead_stage_transition()
327
+ RETURNS TRIGGER AS $$
328
+ BEGIN
329
+ -- Lock row para prevenir lost update
330
+ PERFORM 1 FROM leads WHERE id = NEW.id FOR UPDATE;
331
+ -- ... validação ...
332
+ END;
333
+ $$ LANGUAGE plpgsql;
334
+ ```
335
+
319
336
  ## Ver também
320
337
 
321
338
  - [b2b-saas-architecture](../b2b-saas-architecture/SKILL.md) — schema base