@luanpdd/kit-mcp 1.30.2 → 1.32.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 (365) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +168 -168
  3. package/gates/agent-no-recursive-dispatch.md +84 -82
  4. package/kit/COMANDOS.md +138 -138
  5. package/kit/COMPATIBILITY.md +5 -0
  6. package/kit/README.md +76 -76
  7. package/kit/agents/advisor-researcher.md +107 -106
  8. package/kit/agents/ai-mutation-tester.md +1 -0
  9. package/kit/agents/assumptions-analyzer.md +108 -107
  10. package/kit/agents/audit-log-implementer.md +314 -313
  11. package/kit/agents/auditor-consistencia-isolamento.md +414 -413
  12. package/kit/agents/b2b-saas-architect.md +157 -156
  13. package/kit/agents/burn-rate-forecaster.md +1 -0
  14. package/kit/agents/cascading-failures-auditor.md +299 -298
  15. package/kit/agents/codebase-mapper.md +769 -768
  16. package/kit/agents/crm-pipeline-implementer.md +257 -256
  17. package/kit/agents/debugger.md +814 -813
  18. package/kit/agents/detector-tenant-quente.md +338 -337
  19. package/kit/agents/evolution-go-integrator.md +201 -200
  20. package/kit/agents/example-reviewer.md +22 -21
  21. package/kit/agents/executor.md +565 -564
  22. package/kit/agents/golden-signals-instrumenter.md +1 -0
  23. package/kit/agents/incident-investigator.md +1 -0
  24. package/kit/agents/integration-checker.md +201 -200
  25. package/kit/agents/invite-flow-implementer.md +190 -189
  26. package/kit/agents/legacy-characterizer.md +369 -368
  27. package/kit/agents/lgpd-compliance-auditor.md +296 -295
  28. package/kit/agents/load-shedding-instrumenter.md +1 -0
  29. package/kit/agents/multi-tenant-isolation-auditor.md +254 -253
  30. package/kit/agents/multi-tenant-rls-writer.md +341 -340
  31. package/kit/agents/nyquist-auditor.md +179 -178
  32. package/kit/agents/observability-coverage-auditor.md +316 -315
  33. package/kit/agents/observability-instrumenter.md +1 -0
  34. package/kit/agents/omm-auditor.md +1 -0
  35. package/kit/agents/org-onboarding-implementer.md +224 -223
  36. package/kit/agents/payload-capture-instrumenter.md +274 -273
  37. package/kit/agents/phase-researcher.md +697 -696
  38. package/kit/agents/plan-checker.md +273 -272
  39. package/kit/agents/planner.md +923 -922
  40. package/kit/agents/postmortem-writer.md +1 -0
  41. package/kit/agents/project-researcher.md +653 -652
  42. package/kit/agents/prr-conductor.md +1 -0
  43. package/kit/agents/refactor-safety-auditor.md +405 -404
  44. package/kit/agents/release-pipeline-auditor.md +1 -0
  45. package/kit/agents/research-synthesizer.md +246 -245
  46. package/kit/agents/roadmapper.md +678 -677
  47. package/kit/agents/schema-checker.md +1 -0
  48. package/kit/agents/seam-finder.md +360 -359
  49. package/kit/agents/shotgun-surgery-detector.md +350 -349
  50. package/kit/agents/slo-engineer.md +1 -0
  51. package/kit/agents/storytelling-analyst.md +1 -0
  52. package/kit/agents/supabase-architect.md +1 -0
  53. package/kit/agents/supabase-auth-bootstrapper.md +16 -1
  54. package/kit/agents/supabase-auth-hook-writer.md +418 -0
  55. package/kit/agents/supabase-branching-architect.md +563 -562
  56. package/kit/agents/supabase-cicd-pipeline-implementer.md +778 -777
  57. package/kit/agents/supabase-column-privileges-writer.md +400 -399
  58. package/kit/agents/supabase-edge-fn-tester.md +2 -1
  59. package/kit/agents/supabase-edge-fn-writer.md +2 -1
  60. package/kit/agents/supabase-mfa-implementer.md +439 -0
  61. package/kit/agents/supabase-migration-writer.md +386 -385
  62. package/kit/agents/supabase-oauth-server-implementer.md +507 -0
  63. package/kit/agents/supabase-rbac-implementer.md +393 -392
  64. package/kit/agents/supabase-realtime-implementer.md +364 -363
  65. package/kit/agents/supabase-rls-hardener.md +522 -521
  66. package/kit/agents/supabase-rls-writer.md +324 -323
  67. package/kit/agents/supabase-roles-implementer.md +356 -355
  68. package/kit/agents/supabase-social-auth-implementer.md +451 -0
  69. package/kit/agents/supabase-sso-saml-architect.md +549 -0
  70. package/kit/agents/supabase-storage-implementer.md +1 -0
  71. package/kit/agents/super-admin-implementer.md +282 -281
  72. package/kit/agents/toil-auditor.md +1 -0
  73. package/kit/agents/ui-auditor.md +438 -437
  74. package/kit/agents/ui-checker.md +303 -302
  75. package/kit/agents/ui-researcher.md +356 -355
  76. package/kit/agents/user-profiler.md +176 -175
  77. package/kit/agents/validador-evolucao-schema.md +336 -335
  78. package/kit/agents/verifier.md +729 -728
  79. package/kit/commands/adicionar-backlog.md +75 -75
  80. package/kit/commands/adicionar-fase.md +42 -42
  81. package/kit/commands/adicionar-tarefa.md +45 -45
  82. package/kit/commands/adicionar-testes.md +41 -41
  83. package/kit/commands/ajuda.md +21 -21
  84. package/kit/commands/atualizar.md +37 -37
  85. package/kit/commands/auditar-cascading.md +111 -111
  86. package/kit/commands/auditar-marco.md +179 -179
  87. package/kit/commands/auditar-observabilidade-cobertura.md +183 -183
  88. package/kit/commands/auditar-refactor.md +219 -219
  89. package/kit/commands/auditar-release.md +109 -109
  90. package/kit/commands/auditar-uat.md +23 -23
  91. package/kit/commands/autonomo.md +40 -40
  92. package/kit/commands/branch-pr.md +24 -24
  93. package/kit/commands/burn-rate-status.md +408 -408
  94. package/kit/commands/capturar-payloads.md +193 -193
  95. package/kit/commands/caracterizar.md +212 -212
  96. package/kit/commands/concluir-marco.md +247 -247
  97. package/kit/commands/configuracoes.md +36 -36
  98. package/kit/commands/dados-distribuidos.md +188 -188
  99. package/kit/commands/definir-perfil.md +10 -10
  100. package/kit/commands/depurar.md +190 -190
  101. package/kit/commands/detectar-duplicacao.md +197 -197
  102. package/kit/commands/discutir-fase.md +131 -131
  103. package/kit/commands/encontrar-seams.md +136 -136
  104. package/kit/commands/entrar-discord.md +17 -17
  105. package/kit/commands/estatisticas.md +18 -18
  106. package/kit/commands/example-greeting.md +33 -33
  107. package/kit/commands/executar-fase.md +58 -58
  108. package/kit/commands/expresso.md +56 -56
  109. package/kit/commands/fase-ui.md +34 -34
  110. package/kit/commands/fazer.md +57 -57
  111. package/kit/commands/fio.md +125 -125
  112. package/kit/commands/fluxos-trabalho.md +64 -64
  113. package/kit/commands/forense.md +176 -176
  114. package/kit/commands/gerenciador.md +38 -38
  115. package/kit/commands/inserir-fase.md +31 -31
  116. package/kit/commands/legacy.md +263 -263
  117. package/kit/commands/limpeza.md +17 -17
  118. package/kit/commands/listar-hipoteses-fase.md +45 -45
  119. package/kit/commands/listar-workspaces.md +18 -18
  120. package/kit/commands/load-shedding.md +117 -117
  121. package/kit/commands/mapear-codebase.md +70 -70
  122. package/kit/commands/multi-tenant.md +163 -163
  123. package/kit/commands/nota.md +33 -33
  124. package/kit/commands/novo-marco.md +43 -43
  125. package/kit/commands/novo-projeto.md +41 -41
  126. package/kit/commands/novo-workspace.md +43 -43
  127. package/kit/commands/pausar-trabalho.md +37 -37
  128. package/kit/commands/perfil-usuario.md +45 -45
  129. package/kit/commands/pesquisar-fase.md +195 -195
  130. package/kit/commands/planejar-fase.md +67 -67
  131. package/kit/commands/planejar-lacunas.md +33 -33
  132. package/kit/commands/plantar-ideia.md +25 -25
  133. package/kit/commands/progresso.md +24 -24
  134. package/kit/commands/proximo.md +30 -30
  135. package/kit/commands/publicar.md +490 -490
  136. package/kit/commands/rapido.md +35 -35
  137. package/kit/commands/reaplicar-patches.md +124 -124
  138. package/kit/commands/refactor-seguro.md +321 -321
  139. package/kit/commands/relatorio-sessao.md +19 -19
  140. package/kit/commands/remover-fase.md +31 -31
  141. package/kit/commands/remover-workspace.md +26 -26
  142. package/kit/commands/resumo-marco.md +50 -50
  143. package/kit/commands/retomar-trabalho.md +40 -40
  144. package/kit/commands/revisar-backlog.md +60 -60
  145. package/kit/commands/revisar-ui.md +32 -32
  146. package/kit/commands/revisar.md +37 -37
  147. package/kit/commands/saude.md +21 -21
  148. package/kit/commands/setup-notion.md +93 -93
  149. package/kit/commands/storytelling.md +179 -179
  150. package/kit/commands/supabase.md +21 -1
  151. package/kit/commands/sync-main.md +68 -68
  152. package/kit/commands/validar-fase.md +35 -35
  153. package/kit/commands/verificar-tarefas.md +44 -44
  154. package/kit/commands/verificar-trabalho.md +64 -64
  155. package/kit/file-manifest.json +100 -84
  156. package/kit/framework/bin/lib/commands.cjs +959 -959
  157. package/kit/framework/bin/lib/config.cjs +442 -442
  158. package/kit/framework/bin/lib/core.cjs +1230 -1230
  159. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  160. package/kit/framework/bin/lib/init.cjs +1442 -1442
  161. package/kit/framework/bin/lib/milestone.cjs +252 -252
  162. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  163. package/kit/framework/bin/lib/phase.cjs +888 -888
  164. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  165. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  166. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  167. package/kit/framework/bin/lib/security.cjs +382 -382
  168. package/kit/framework/bin/lib/state.cjs +1031 -1031
  169. package/kit/framework/bin/lib/template.cjs +222 -222
  170. package/kit/framework/bin/lib/uat.cjs +282 -282
  171. package/kit/framework/bin/lib/verify.cjs +888 -888
  172. package/kit/framework/bin/lib/workstream.cjs +491 -491
  173. package/kit/framework/bin/tools.cjs +918 -918
  174. package/kit/framework/commands/workstreams.md +63 -63
  175. package/kit/framework/references/checkpoints.md +778 -778
  176. package/kit/framework/references/continuation-format.md +249 -249
  177. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  178. package/kit/framework/references/git-integration.md +295 -295
  179. package/kit/framework/references/git-planning-commit.md +38 -38
  180. package/kit/framework/references/model-profile-resolution.md +36 -36
  181. package/kit/framework/references/model-profiles.md +139 -139
  182. package/kit/framework/references/phase-argument-parsing.md +61 -61
  183. package/kit/framework/references/planning-config.md +202 -202
  184. package/kit/framework/references/questioning.md +162 -162
  185. package/kit/framework/references/tdd.md +263 -263
  186. package/kit/framework/references/ui-brand.md +160 -160
  187. package/kit/framework/references/user-profiling.md +657 -657
  188. package/kit/framework/references/verification-patterns.md +612 -612
  189. package/kit/framework/references/workstream-flag.md +58 -58
  190. package/kit/framework/templates/DEBUG.md +164 -164
  191. package/kit/framework/templates/UAT.md +265 -265
  192. package/kit/framework/templates/UI-SPEC.md +100 -100
  193. package/kit/framework/templates/VALIDATION.md +76 -76
  194. package/kit/framework/templates/claude-md.md +122 -122
  195. package/kit/framework/templates/codebase/architecture.md +185 -185
  196. package/kit/framework/templates/codebase/concerns.md +205 -205
  197. package/kit/framework/templates/codebase/conventions.md +204 -204
  198. package/kit/framework/templates/codebase/integrations.md +192 -192
  199. package/kit/framework/templates/codebase/stack.md +158 -158
  200. package/kit/framework/templates/codebase/structure.md +199 -199
  201. package/kit/framework/templates/codebase/testing.md +301 -301
  202. package/kit/framework/templates/config.json +44 -44
  203. package/kit/framework/templates/context.md +352 -352
  204. package/kit/framework/templates/continue-here.md +78 -78
  205. package/kit/framework/templates/copilot-instructions.md +7 -7
  206. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  207. package/kit/framework/templates/dev-preferences.md +20 -20
  208. package/kit/framework/templates/discovery.md +146 -146
  209. package/kit/framework/templates/discussion-log.md +63 -63
  210. package/kit/framework/templates/milestone-archive.md +123 -123
  211. package/kit/framework/templates/milestone.md +115 -115
  212. package/kit/framework/templates/phase-prompt.md +610 -610
  213. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  214. package/kit/framework/templates/project.md +186 -186
  215. package/kit/framework/templates/requirements.md +231 -231
  216. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  217. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  218. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  219. package/kit/framework/templates/research-project/STACK.md +120 -120
  220. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  221. package/kit/framework/templates/research.md +419 -419
  222. package/kit/framework/templates/retrospective.md +54 -54
  223. package/kit/framework/templates/roadmap.md +202 -202
  224. package/kit/framework/templates/state.md +176 -176
  225. package/kit/framework/templates/summary-complex.md +59 -59
  226. package/kit/framework/templates/summary-minimal.md +41 -41
  227. package/kit/framework/templates/summary-standard.md +48 -48
  228. package/kit/framework/templates/summary.md +209 -209
  229. package/kit/framework/templates/user-profile.md +146 -146
  230. package/kit/framework/templates/user-setup.md +256 -256
  231. package/kit/framework/templates/verification-report.md +258 -258
  232. package/kit/framework/workflows/add-phase.md +112 -112
  233. package/kit/framework/workflows/add-tests.md +351 -351
  234. package/kit/framework/workflows/add-todo.md +158 -158
  235. package/kit/framework/workflows/audit-milestone.md +340 -340
  236. package/kit/framework/workflows/audit-uat.md +109 -109
  237. package/kit/framework/workflows/autonomous.md +891 -891
  238. package/kit/framework/workflows/check-todos.md +177 -177
  239. package/kit/framework/workflows/cleanup.md +152 -152
  240. package/kit/framework/workflows/complete-milestone.md +696 -696
  241. package/kit/framework/workflows/diagnose-issues.md +231 -231
  242. package/kit/framework/workflows/discovery-phase.md +289 -289
  243. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  244. package/kit/framework/workflows/discuss-phase.md +784 -784
  245. package/kit/framework/workflows/do.md +104 -104
  246. package/kit/framework/workflows/execute-phase.md +838 -838
  247. package/kit/framework/workflows/execute-plan.md +510 -510
  248. package/kit/framework/workflows/fast.md +102 -102
  249. package/kit/framework/workflows/forensics.md +265 -265
  250. package/kit/framework/workflows/health.md +181 -181
  251. package/kit/framework/workflows/help.md +619 -619
  252. package/kit/framework/workflows/insert-phase.md +130 -130
  253. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  254. package/kit/framework/workflows/list-workspaces.md +56 -56
  255. package/kit/framework/workflows/manager.md +362 -362
  256. package/kit/framework/workflows/map-codebase.md +377 -377
  257. package/kit/framework/workflows/milestone-summary.md +223 -223
  258. package/kit/framework/workflows/new-milestone.md +486 -486
  259. package/kit/framework/workflows/new-project.md +1159 -1159
  260. package/kit/framework/workflows/new-workspace.md +237 -237
  261. package/kit/framework/workflows/next.md +97 -97
  262. package/kit/framework/workflows/node-repair.md +92 -92
  263. package/kit/framework/workflows/note.md +156 -156
  264. package/kit/framework/workflows/pause-work.md +176 -176
  265. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  266. package/kit/framework/workflows/plan-phase.md +765 -765
  267. package/kit/framework/workflows/plant-seed.md +169 -169
  268. package/kit/framework/workflows/pr-branch.md +129 -129
  269. package/kit/framework/workflows/profile-user.md +450 -450
  270. package/kit/framework/workflows/progress.md +507 -507
  271. package/kit/framework/workflows/quick.md +757 -757
  272. package/kit/framework/workflows/remove-phase.md +155 -155
  273. package/kit/framework/workflows/remove-workspace.md +90 -90
  274. package/kit/framework/workflows/research-phase.md +82 -82
  275. package/kit/framework/workflows/resume-project.md +326 -326
  276. package/kit/framework/workflows/review.md +228 -228
  277. package/kit/framework/workflows/session-report.md +146 -146
  278. package/kit/framework/workflows/settings.md +283 -283
  279. package/kit/framework/workflows/ship.md +228 -228
  280. package/kit/framework/workflows/stats.md +60 -60
  281. package/kit/framework/workflows/transition.md +671 -671
  282. package/kit/framework/workflows/ui-phase.md +302 -302
  283. package/kit/framework/workflows/ui-review.md +165 -165
  284. package/kit/framework/workflows/update.md +323 -323
  285. package/kit/framework/workflows/validate-phase.md +174 -174
  286. package/kit/framework/workflows/verify-phase.md +252 -252
  287. package/kit/framework/workflows/verify-work.md +637 -637
  288. package/kit/hooks/check-update.js +118 -118
  289. package/kit/hooks/context-monitor.js +163 -163
  290. package/kit/hooks/kit-attribution-reminder.cjs +29 -50
  291. package/kit/hooks/kit-router.cjs +137 -0
  292. package/kit/hooks/prompt-guard.js +103 -103
  293. package/kit/hooks/statusline.js +125 -125
  294. package/kit/hooks/workflow-guard.js +101 -101
  295. package/kit/settings.json +45 -45
  296. package/kit/skills/ai-prompt-characterization/SKILL.md +335 -335
  297. package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -447
  298. package/kit/skills/audit-log-multi-tenant/SKILL.md +340 -340
  299. package/kit/skills/b2b-saas-architecture/SKILL.md +300 -300
  300. package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -385
  301. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +343 -343
  302. package/kit/skills/escolha-modelo-consistencia/SKILL.md +494 -494
  303. package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -448
  304. package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -322
  305. package/kit/skills/example-skill/SKILL.md +42 -42
  306. package/kit/skills/legacy-api-only-applications/SKILL.md +358 -358
  307. package/kit/skills/legacy-characterization-tests/SKILL.md +330 -330
  308. package/kit/skills/legacy-effect-analysis/SKILL.md +331 -331
  309. package/kit/skills/legacy-extract-class/SKILL.md +203 -203
  310. package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -252
  311. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -460
  312. package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -286
  313. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -434
  314. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -270
  315. package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -340
  316. package/kit/skills/member-invite-flow/SKILL.md +305 -305
  317. package/kit/skills/member-management-react-shadcn/SKILL.md +328 -328
  318. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +316 -316
  319. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +342 -342
  320. package/kit/skills/org-onboarding-flow/SKILL.md +257 -257
  321. package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -349
  322. package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -271
  323. package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -552
  324. package/kit/skills/pre-refactor-characterization/SKILL.md +421 -421
  325. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +338 -338
  326. package/kit/skills/streams-eventos-cdc/SKILL.md +711 -711
  327. package/kit/skills/supabase-auth-hardening/SKILL.md +674 -0
  328. package/kit/skills/supabase-auth-hooks/SKILL.md +875 -0
  329. package/kit/skills/supabase-auth-methods/SKILL.md +486 -0
  330. package/kit/skills/supabase-auth-sessions/SKILL.md +579 -0
  331. package/kit/skills/supabase-auth-ssr/SKILL.md +60 -14
  332. package/kit/skills/supabase-branching-workflow/SKILL.md +544 -544
  333. package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -880
  334. package/kit/skills/supabase-column-level-security/SKILL.md +426 -426
  335. package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -807
  336. package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -472
  337. package/kit/skills/supabase-edge-functions/SKILL.md +1 -1
  338. package/kit/skills/supabase-edge-functions-auth/SKILL.md +1 -1
  339. package/kit/skills/supabase-edge-functions-limits/SKILL.md +1 -1
  340. package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +1 -1
  341. package/kit/skills/supabase-edge-functions-testing/SKILL.md +1 -1
  342. package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +1 -1
  343. package/kit/skills/supabase-enterprise-sso-saml/SKILL.md +545 -0
  344. package/kit/skills/supabase-jwt-signing-keys/SKILL.md +399 -0
  345. package/kit/skills/supabase-mfa/SKILL.md +488 -0
  346. package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
  347. package/kit/skills/supabase-migrations/SKILL.md +297 -297
  348. package/kit/skills/supabase-oauth-server/SKILL.md +537 -0
  349. package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -1053
  350. package/kit/skills/supabase-postgres-roles/SKILL.md +392 -392
  351. package/kit/skills/supabase-realtime/SKILL.md +460 -460
  352. package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -418
  353. package/kit/skills/supabase-rls-policies/SKILL.md +635 -635
  354. package/kit/skills/supabase-social-oauth/SKILL.md +480 -0
  355. package/kit/skills/supabase-third-party-auth/SKILL.md +450 -0
  356. package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
  357. package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
  358. package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
  359. package/package.json +1 -1
  360. package/src/core/kit.js +216 -216
  361. package/src/core/reflect.js +247 -247
  362. package/src/core/reverse-sync.js +372 -372
  363. package/src/core/sync.js +437 -418
  364. package/src/core/watch.js +121 -121
  365. package/src/mcp-server/index.js +794 -746
@@ -1,385 +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…
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)
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…
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)