@evertonferreira96/agents 0.0.0-stage → 0.1.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 (258) hide show
  1. package/.claude/agents/api.md +45 -0
  2. package/.claude/agents/app.md +41 -0
  3. package/.claude/agents/architect.md +49 -0
  4. package/.claude/agents/infra.md +27 -0
  5. package/.claude/agents/product.md +58 -0
  6. package/.claude/agents/quick.md +33 -0
  7. package/.claude/agents/security.md +39 -0
  8. package/.claude/agents/ui.md +54 -0
  9. package/.claude/agents/verifier.md +56 -0
  10. package/.claude/agents/web.md +41 -0
  11. package/.claude/hooks/config-update-check.mjs +14 -0
  12. package/.claude/hooks/context-budget.mjs +51 -0
  13. package/.claude/hooks/graphify-update.sh +16 -0
  14. package/.claude/hooks/guard-bash.sh +64 -0
  15. package/.claude/hooks/orchestrator-guard.sh +49 -0
  16. package/.claude/hooks/post-compact.sh +26 -0
  17. package/.claude/hooks/readonly-guard.sh +54 -0
  18. package/.claude/hooks/stop-verify.sh +52 -0
  19. package/.claude/rules/api.md +21 -0
  20. package/.claude/rules/db.md +9 -0
  21. package/.claude/rules/delivery-diagram.md +16 -0
  22. package/.claude/rules/git-workflow.md +34 -0
  23. package/.claude/rules/graphify.md +3 -0
  24. package/.claude/rules/mobile.md +22 -0
  25. package/.claude/rules/tickets.md +9 -0
  26. package/.claude/rules/ui.md +14 -0
  27. package/.claude/rules/web.md +27 -0
  28. package/.claude/settings.json +114 -0
  29. package/.claude/skills/animate/RECIPES.md +324 -0
  30. package/.claude/skills/animate/SKILL.md +208 -0
  31. package/.claude/skills/animate/agents/openai.yaml +5 -0
  32. package/.claude/skills/animate-expo/RECIPES.md +385 -0
  33. package/.claude/skills/animate-expo/SKILL.md +264 -0
  34. package/.claude/skills/animate-expo/agents/openai.yaml +5 -0
  35. package/.claude/skills/animation-performance/SKILL.md +223 -0
  36. package/.claude/skills/animation-performance/agents/openai.yaml +5 -0
  37. package/.claude/skills/apple-design/SKILL.md +291 -0
  38. package/.claude/skills/apple-design/agents/openai.yaml +5 -0
  39. package/.claude/skills/arch-api/SKILL.md +291 -0
  40. package/.claude/skills/arch-api/agents/openai.yaml +3 -0
  41. package/.claude/skills/arch-api/references/common.md +174 -0
  42. package/.claude/skills/arch-api/references/contract.md +289 -0
  43. package/.claude/skills/arch-api/references/domain.md +301 -0
  44. package/.claude/skills/arch-api/references/endpoints.md +184 -0
  45. package/.claude/skills/arch-api/references/review.md +228 -0
  46. package/.claude/skills/arch-api/references/security.md +151 -0
  47. package/.claude/skills/arch-app/SKILL.md +278 -0
  48. package/.claude/skills/arch-app/agents/openai.yaml +3 -0
  49. package/.claude/skills/arch-app/references/navigation-session.md +257 -0
  50. package/.claude/skills/arch-app/references/review.md +155 -0
  51. package/.claude/skills/arch-app/references/screens.md +214 -0
  52. package/.claude/skills/arch-app/references/services.md +301 -0
  53. package/.claude/skills/arch-app/references/ui-mobile.md +124 -0
  54. package/.claude/skills/arch-db/SKILL.md +181 -0
  55. package/.claude/skills/arch-db/agents/openai.yaml +3 -0
  56. package/.claude/skills/arch-db/references/indexes-queries.md +251 -0
  57. package/.claude/skills/arch-db/references/migrations.md +191 -0
  58. package/.claude/skills/arch-db/references/modeling.md +214 -0
  59. package/.claude/skills/arch-db/references/mysql.md +262 -0
  60. package/.claude/skills/arch-db/references/pooling-ops.md +112 -0
  61. package/.claude/skills/arch-db/references/review.md +143 -0
  62. package/.claude/skills/arch-db/references/transactions-locks.md +141 -0
  63. package/.claude/skills/arch-web/SKILL.md +307 -0
  64. package/.claude/skills/arch-web/agents/openai.yaml +3 -0
  65. package/.claude/skills/arch-web/references/auth-session-env.md +165 -0
  66. package/.claude/skills/arch-web/references/blocks.md +198 -0
  67. package/.claude/skills/arch-web/references/cache.md +124 -0
  68. package/.claude/skills/arch-web/references/review.md +142 -0
  69. package/.claude/skills/arch-web/references/services.md +297 -0
  70. package/.claude/skills/arch-web/references/tests.md +159 -0
  71. package/.claude/skills/arch-web/references/ui-forms.md +143 -0
  72. package/.claude/skills/break-ui/CATALOG.md +135 -0
  73. package/.claude/skills/break-ui/SKILL.md +176 -0
  74. package/.claude/skills/break-ui/agents/openai.yaml +5 -0
  75. package/.claude/skills/brief/SKILL.md +91 -0
  76. package/.claude/skills/brief/agents/openai.yaml +3 -0
  77. package/.claude/skills/diagnosing-bugs/SKILL.md +138 -0
  78. package/.claude/skills/diagnosing-bugs/agents/openai.yaml +3 -0
  79. package/.claude/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
  80. package/.claude/skills/emil-design-eng/SKILL.md +675 -0
  81. package/.claude/skills/emil-design-eng/agents/openai.yaml +5 -0
  82. package/.claude/skills/find-animation-opportunities/SKILL.md +141 -0
  83. package/.claude/skills/find-animation-opportunities/agents/openai.yaml +5 -0
  84. package/.claude/skills/graphify/.graphify_version +1 -0
  85. package/.claude/skills/graphify/SKILL.md +742 -0
  86. package/.claude/skills/graphify/agents/openai.yaml +3 -0
  87. package/.claude/skills/graphify/references/add-watch.md +56 -0
  88. package/.claude/skills/graphify/references/exports.md +91 -0
  89. package/.claude/skills/graphify/references/extraction-spec.md +70 -0
  90. package/.claude/skills/graphify/references/github-and-merge.md +46 -0
  91. package/.claude/skills/graphify/references/hooks.md +33 -0
  92. package/.claude/skills/graphify/references/query.md +317 -0
  93. package/.claude/skills/graphify/references/transcribe.md +52 -0
  94. package/.claude/skills/graphify/references/update.md +221 -0
  95. package/.claude/skills/grill-me/SKILL.md +7 -0
  96. package/.claude/skills/grill-me/agents/openai.yaml +5 -0
  97. package/.claude/skills/grill-with-docs/SKILL.md +64 -0
  98. package/.claude/skills/grill-with-docs/agents/openai.yaml +5 -0
  99. package/.claude/skills/grilling/SKILL.md +30 -0
  100. package/.claude/skills/grilling/agents/openai.yaml +3 -0
  101. package/.claude/skills/handoff/SKILL.md +28 -0
  102. package/.claude/skills/handoff/agents/openai.yaml +5 -0
  103. package/.claude/skills/impeccable/SKILL.md +89 -0
  104. package/.claude/skills/impeccable/agents/openai.yaml +5 -0
  105. package/.claude/skills/impeccable/reference/adapt.md +318 -0
  106. package/.claude/skills/impeccable/reference/adapt.native.md +58 -0
  107. package/.claude/skills/impeccable/reference/android.md +46 -0
  108. package/.claude/skills/impeccable/reference/animate.md +89 -0
  109. package/.claude/skills/impeccable/reference/audit.md +137 -0
  110. package/.claude/skills/impeccable/reference/audit.native.md +139 -0
  111. package/.claude/skills/impeccable/reference/bolder.md +33 -0
  112. package/.claude/skills/impeccable/reference/clarify.md +94 -0
  113. package/.claude/skills/impeccable/reference/colorize.md +86 -0
  114. package/.claude/skills/impeccable/reference/component-review.md +63 -0
  115. package/.claude/skills/impeccable/reference/craft-floor.md +44 -0
  116. package/.claude/skills/impeccable/reference/craft.md +5 -0
  117. package/.claude/skills/impeccable/reference/critique.md +809 -0
  118. package/.claude/skills/impeccable/reference/degraded/asset-producer.md +42 -0
  119. package/.claude/skills/impeccable/reference/degraded/documenter.md +24 -0
  120. package/.claude/skills/impeccable/reference/degraded/finish-reviewer.md +38 -0
  121. package/.claude/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  122. package/.claude/skills/impeccable/reference/delight.md +70 -0
  123. package/.claude/skills/impeccable/reference/distill.md +111 -0
  124. package/.claude/skills/impeccable/reference/doctor.md +54 -0
  125. package/.claude/skills/impeccable/reference/document.md +416 -0
  126. package/.claude/skills/impeccable/reference/extract.md +69 -0
  127. package/.claude/skills/impeccable/reference/generate.md +101 -0
  128. package/.claude/skills/impeccable/reference/harden.md +345 -0
  129. package/.claude/skills/impeccable/reference/hooks.md +113 -0
  130. package/.claude/skills/impeccable/reference/init.md +131 -0
  131. package/.claude/skills/impeccable/reference/ios.md +51 -0
  132. package/.claude/skills/impeccable/reference/layout.md +84 -0
  133. package/.claude/skills/impeccable/reference/live-setup.md +104 -0
  134. package/.claude/skills/impeccable/reference/live.md +325 -0
  135. package/.claude/skills/impeccable/reference/mode-operate.md +21 -0
  136. package/.claude/skills/impeccable/reference/mode-persuade.md +19 -0
  137. package/.claude/skills/impeccable/reference/mode-read.md +21 -0
  138. package/.claude/skills/impeccable/reference/new-work.md +154 -0
  139. package/.claude/skills/impeccable/reference/onboard.md +234 -0
  140. package/.claude/skills/impeccable/reference/operate.md +61 -0
  141. package/.claude/skills/impeccable/reference/optimize.md +258 -0
  142. package/.claude/skills/impeccable/reference/overdrive.md +127 -0
  143. package/.claude/skills/impeccable/reference/polish.md +105 -0
  144. package/.claude/skills/impeccable/reference/quieter.md +99 -0
  145. package/.claude/skills/impeccable/reference/region-map.md +26 -0
  146. package/.claude/skills/impeccable/reference/routing.md +24 -0
  147. package/.claude/skills/impeccable/reference/shape.md +59 -0
  148. package/.claude/skills/impeccable/reference/typeset.md +80 -0
  149. package/.claude/skills/impeccable/reference/visualize.md +47 -0
  150. package/.claude/skills/impeccable/scripts/VERSION +1 -0
  151. package/.claude/skills/impeccable/scripts/command-metadata.json +98 -0
  152. package/.claude/skills/impeccable/scripts/data/font-index-failures.json +121 -0
  153. package/.claude/skills/impeccable/scripts/data/font-index.json +1 -0
  154. package/.claude/skills/impeccable/scripts/impeccable +206 -0
  155. package/.claude/skills/impeccable/scripts/impeccable.cmd +214 -0
  156. package/.claude/skills/impeccable/scripts/live-browser-dom.js +281 -0
  157. package/.claude/skills/impeccable/scripts/live-browser-ignores.js +242 -0
  158. package/.claude/skills/impeccable/scripts/live-browser-session.js +148 -0
  159. package/.claude/skills/impeccable/scripts/live-browser.js +13532 -0
  160. package/.claude/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
  161. package/.claude/skills/improve-animations/AUDIT.md +115 -0
  162. package/.claude/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
  163. package/.claude/skills/improve-animations/SKILL.md +110 -0
  164. package/.claude/skills/improve-animations/agents/openai.yaml +5 -0
  165. package/.claude/skills/mobile-native/SKILL.md +311 -0
  166. package/.claude/skills/mobile-native/agents/openai.yaml +3 -0
  167. package/.claude/skills/orchestrate/SKILL.md +181 -0
  168. package/.claude/skills/orchestrate/agents/openai.yaml +3 -0
  169. package/.claude/skills/pick-ui-library/SKILL.md +85 -0
  170. package/.claude/skills/pick-ui-library/agents/openai.yaml +5 -0
  171. package/.claude/skills/ponytail/SKILL.md +52 -0
  172. package/.claude/skills/ponytail/agents/openai.yaml +3 -0
  173. package/.claude/skills/ponytail-audit/SKILL.md +103 -0
  174. package/.claude/skills/ponytail-audit/agents/openai.yaml +3 -0
  175. package/.claude/skills/ponytail-debt/SKILL.md +44 -0
  176. package/.claude/skills/ponytail-debt/agents/openai.yaml +3 -0
  177. package/.claude/skills/ponytail-review/SKILL.md +101 -0
  178. package/.claude/skills/ponytail-review/agents/openai.yaml +3 -0
  179. package/.claude/skills/postgres-best-practices/SKILL.md +42 -0
  180. package/.claude/skills/postgres-best-practices/agents/openai.yaml +3 -0
  181. package/.claude/skills/postgres-best-practices/references/backup-restore.md +372 -0
  182. package/.claude/skills/postgres-best-practices/references/bulk-loading.md +315 -0
  183. package/.claude/skills/postgres-best-practices/references/connection-pooling.md +310 -0
  184. package/.claude/skills/postgres-best-practices/references/hot-standby.md +221 -0
  185. package/.claude/skills/postgres-best-practices/references/indexing.md +318 -0
  186. package/.claude/skills/postgres-best-practices/references/logical-replication.md +610 -0
  187. package/.claude/skills/postgres-best-practices/references/major-version-upgrades.md +334 -0
  188. package/.claude/skills/postgres-best-practices/references/performance-diagnostics.md +553 -0
  189. package/.claude/skills/postgres-best-practices/references/query-optimization.md +294 -0
  190. package/.claude/skills/postgres-best-practices/references/query-patterns.md +566 -0
  191. package/.claude/skills/postgres-best-practices/references/schema-design.md +362 -0
  192. package/.claude/skills/postgres-best-practices/references/security-roles.md +489 -0
  193. package/.claude/skills/postgres-best-practices/references/transaction-isolation.md +344 -0
  194. package/.claude/skills/project-docs/SKILL.md +330 -0
  195. package/.claude/skills/project-docs/agents/openai.yaml +3 -0
  196. package/.claude/skills/prototype/PICKER.md +197 -0
  197. package/.claude/skills/prototype/SKILL.md +98 -0
  198. package/.claude/skills/prototype/agents/openai.yaml +5 -0
  199. package/.claude/skills/retro/SKILL.md +43 -0
  200. package/.claude/skills/retro/agents/openai.yaml +5 -0
  201. package/.claude/skills/review/SKILL.md +74 -0
  202. package/.claude/skills/review/agents/openai.yaml +3 -0
  203. package/.claude/skills/review/references/smells.md +23 -0
  204. package/.claude/skills/review-animations/SKILL.md +120 -0
  205. package/.claude/skills/review-animations/STANDARDS.md +187 -0
  206. package/.claude/skills/review-animations/agents/openai.yaml +5 -0
  207. package/.claude/skills/tdd/SKILL.md +38 -0
  208. package/.claude/skills/tdd/agents/openai.yaml +3 -0
  209. package/.claude/skills/tdd/mocking.md +59 -0
  210. package/.claude/skills/tdd/tests.md +77 -0
  211. package/.claude/skills/triage/SKILL.md +58 -0
  212. package/.claude/skills/triage/agents/openai.yaml +3 -0
  213. package/.claude/skills/ui-ux-pro-max/SKILL.md +378 -0
  214. package/.claude/skills/ui-ux-pro-max/agents/openai.yaml +5 -0
  215. package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
  216. package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
  217. package/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -0
  218. package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
  219. package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
  220. package/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  221. package/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  222. package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  223. package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  224. package/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  225. package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  226. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  227. package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  228. package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  229. package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  230. package/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  231. package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  232. package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  233. package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  234. package/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -0
  235. package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
  236. package/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  237. package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  238. package/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  239. package/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -0
  240. package/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  241. package/.claude/skills/ui-ux-pro-max/scripts/search.py +114 -0
  242. package/.claude/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
  243. package/.claude/skills/writing-for-agents/SKILL.md +81 -0
  244. package/.claude/skills/writing-for-agents/agents/openai.yaml +3 -0
  245. package/.worktreeinclude +3 -0
  246. package/AGENTS.md +44 -0
  247. package/README.md +14 -2
  248. package/VERSION +1 -0
  249. package/bin/agents.mjs +45 -0
  250. package/docs/adr/README.md +31 -0
  251. package/docs/brief-template.md +3 -0
  252. package/docs/plans/README.md +73 -0
  253. package/package.json +46 -4
  254. package/scripts/bootstrap.mjs +188 -0
  255. package/scripts/check-catalog.mjs +43 -0
  256. package/scripts/skills-update.mjs +51 -0
  257. package/skills-lock.json +269 -0
  258. package/templates/CLAUDE.project.md +41 -0
@@ -0,0 +1,610 @@
1
+ # Logical Replication & Migrations Reference
2
+
3
+ ## Contents
4
+ - Overview and prerequisites
5
+ - Publisher setup
6
+ - Subscriber setup
7
+ - Managing publications (add/remove tables)
8
+ - Managing subscriptions
9
+ - Monitoring replication progress
10
+ - Schema changes during replication
11
+ - Live migration patterns
12
+ - Troubleshooting
13
+
14
+ ## Overview
15
+
16
+ Logical replication streams row-level changes (INSERT, UPDATE, DELETE) from a **publisher** to one or more **subscribers** using the publish/subscribe model. Unlike physical replication, it:
17
+
18
+ - Replicates specific tables, not the entire cluster
19
+ - Allows different indexes, security policies, or schemas on the subscriber
20
+ - Works across different PG major versions (useful for upgrades)
21
+ - Allows the subscriber to be writable (for other tables)
22
+
23
+ ### Prerequisites
24
+
25
+ **On the publisher:**
26
+ - `wal_level = logical` (requires restart if changing)
27
+ - Sufficient `max_replication_slots` (one per subscription)
28
+ - Sufficient `max_wal_senders` (one per subscription + headroom)
29
+ - Tables must have a replica identity (primary key by default)
30
+ - The replication role needs `REPLICATION` privilege, plus `USAGE` on the schema and `SELECT` on replicated tables
31
+
32
+ **On the subscriber:**
33
+ - Target tables must already exist with compatible schema
34
+ - Sufficient `max_logical_replication_workers`
35
+ - Sufficient `max_worker_processes`
36
+ - The role needs `pg_create_subscription` membership (PG16+)
37
+
38
+ Check current settings:
39
+
40
+ ```sql
41
+ SHOW wal_level; -- must be 'logical'
42
+ SHOW max_replication_slots; -- must have available slots
43
+ SHOW max_wal_senders; -- must have available senders
44
+
45
+ -- Check how many slots are already in use
46
+ SELECT count(*) AS used_slots FROM pg_replication_slots;
47
+ ```
48
+
49
+ **How many slots does a subscription need?**
50
+
51
+ During **initial sync**, Postgres creates one temporary replication slot per table being copied in parallel, plus one permanent slot for the subscription itself. For example, a subscription syncing 10 tables with `max_sync_workers_per_subscription = 2` (default) uses up to **3 slots** at peak: 1 permanent + 2 temporary for parallel table copy.
52
+
53
+ Once initial sync completes and the subscription enters **streaming (CDC) mode**, only the **1 permanent slot** per subscription is used. The temporary per-table slots are dropped.
54
+
55
+ **Rule of thumb**: `max_replication_slots` >= (number of subscriptions) + (max_sync_workers_per_subscription) + headroom for physical replication slots. The default of 10 is sufficient for most setups.
56
+
57
+ ## Publisher Setup
58
+
59
+ ### Create a Publication
60
+
61
+ ```sql
62
+ -- Publish specific tables
63
+ CREATE PUBLICATION my_pub FOR TABLE orders, customers, products;
64
+
65
+ -- Publish all tables in a schema (PG15+)
66
+ CREATE PUBLICATION my_pub FOR TABLES IN SCHEMA public;
67
+
68
+ -- Publish all tables in the database
69
+ -- WARNING: FOR ALL TABLES prevents later ADD/DROP TABLE modifications.
70
+ -- Prefer listing tables explicitly if you may need to change the set later.
71
+ CREATE PUBLICATION my_pub FOR ALL TABLES;
72
+
73
+ -- Publish only specific operations
74
+ CREATE PUBLICATION inserts_only FOR TABLE events
75
+ WITH (publish = 'insert');
76
+
77
+ -- Publish with row filter (PG15+)
78
+ CREATE PUBLICATION active_orders FOR TABLE orders
79
+ WHERE (status = 'active');
80
+
81
+ -- Publish specific columns only (PG15+)
82
+ CREATE PUBLICATION partial_customers FOR TABLE customers (id, name, email);
83
+ ```
84
+
85
+ ### Replica Identity
86
+
87
+ Logical replication needs a way to identify rows for UPDATE and DELETE. By default, it uses the primary key.
88
+
89
+ ```sql
90
+ -- Check current replica identity
91
+ SELECT relname, relreplident
92
+ FROM pg_class
93
+ WHERE relname IN ('orders', 'customers');
94
+ -- 'd' = default (primary key), 'f' = full, 'n' = nothing, 'i' = index
95
+
96
+ -- If a table has no primary key, use FULL (sends entire old row)
97
+ ALTER TABLE legacy_table REPLICA IDENTITY FULL;
98
+
99
+ -- Or use a unique index
100
+ CREATE UNIQUE INDEX idx_legacy_key ON legacy_table(external_id);
101
+ ALTER TABLE legacy_table REPLICA IDENTITY USING INDEX idx_legacy_key;
102
+ ```
103
+
104
+ **Without a replica identity, UPDATE and DELETE will fail on the publisher** for that table.
105
+
106
+ ### Grant Permissions to the Replication Role
107
+
108
+ The replication role needs schema access and SELECT on the published tables:
109
+
110
+ ```sql
111
+ GRANT USAGE ON SCHEMA public TO repl_user;
112
+ GRANT SELECT ON ALL TABLES IN SCHEMA public TO repl_user;
113
+
114
+ -- Also grant for future tables
115
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO repl_user;
116
+ ```
117
+
118
+ ### Decoder Plugins
119
+
120
+ Postgres supports logical decoding output plugins, including:
121
+ - **`pgoutput`** (default): built into Postgres, used by native logical replication
122
+ - **`wal2json`**: an optional third-party plugin that converts WAL to JSON format for CDC integrations
123
+
124
+ The decoder is specified when creating a replication slot manually:
125
+
126
+ ```sql
127
+ SELECT pg_create_logical_replication_slot('my_slot', 'pgoutput');
128
+ ```
129
+
130
+ After installing `wal2json` on the database server:
131
+
132
+ ```sql
133
+ SELECT pg_create_logical_replication_slot('my_slot', 'wal2json');
134
+ ```
135
+
136
+ When using `CREATE SUBSCRIPTION`, the default `pgoutput` plugin is used automatically.
137
+
138
+ ### List Publications
139
+
140
+ ```sql
141
+ SELECT * FROM pg_publication;
142
+
143
+ -- See which tables are in a publication
144
+ SELECT pubname, schemaname, tablename
145
+ FROM pg_publication_tables
146
+ ORDER BY pubname, tablename;
147
+ ```
148
+
149
+ ## Subscriber Setup
150
+
151
+ ### Create a Subscription
152
+
153
+ ```sql
154
+ -- Basic subscription (triggers initial data copy)
155
+ CREATE SUBSCRIPTION my_sub
156
+ CONNECTION 'host=publisher_host dbname=source_db user=repl_user password=...'
157
+ PUBLICATION my_pub;
158
+
159
+ -- Subscribe without initial data copy (tables already have data)
160
+ CREATE SUBSCRIPTION my_sub
161
+ CONNECTION 'host=publisher_host dbname=source_db user=repl_user password=...'
162
+ PUBLICATION my_pub
163
+ WITH (copy_data = false);
164
+
165
+ -- Subscribe to multiple publications
166
+ CREATE SUBSCRIPTION my_sub
167
+ CONNECTION 'host=publisher_host dbname=source_db user=repl_user password=...'
168
+ PUBLICATION pub_orders, pub_customers;
169
+
170
+ -- Create disabled (activate later)
171
+ CREATE SUBSCRIPTION my_sub
172
+ CONNECTION 'host=publisher_host dbname=source_db user=repl_user password=...'
173
+ PUBLICATION my_pub
174
+ WITH (enabled = false);
175
+ ```
176
+
177
+ ### List Subscriptions
178
+
179
+ ```sql
180
+ SELECT * FROM pg_subscription;
181
+
182
+ -- Subscription status and worker info
183
+ SELECT subname, pid, relid::regclass, received_lsn, latest_end_lsn,
184
+ latest_end_time
185
+ FROM pg_stat_subscription;
186
+ ```
187
+
188
+ ## Managing Publications
189
+
190
+ ### Add Tables
191
+
192
+ ```sql
193
+ ALTER PUBLICATION my_pub ADD TABLE new_table;
194
+
195
+ -- Add with row filter (PG15+)
196
+ ALTER PUBLICATION my_pub ADD TABLE audit_log WHERE (created_at > '2024-01-01');
197
+
198
+ -- Add multiple tables at once
199
+ ALTER PUBLICATION my_pub ADD TABLE table_a, table_b, table_c;
200
+ ```
201
+
202
+ ### Remove Tables
203
+
204
+ ```sql
205
+ ALTER PUBLICATION my_pub DROP TABLE old_table;
206
+
207
+ -- Remove multiple
208
+ ALTER PUBLICATION my_pub DROP TABLE table_a, table_b;
209
+ ```
210
+
211
+ ### Replace Entire Table List
212
+
213
+ ```sql
214
+ ALTER PUBLICATION my_pub SET TABLE orders, customers, products, shipments;
215
+ ```
216
+
217
+ ### Change Published Operations
218
+
219
+ ```sql
220
+ -- Only publish inserts and updates (no deletes)
221
+ ALTER PUBLICATION my_pub SET (publish = 'insert, update');
222
+
223
+ -- Restore all operations
224
+ ALTER PUBLICATION my_pub SET (publish = 'insert, update, delete, truncate');
225
+ ```
226
+
227
+ ### After Adding Tables — Refresh the Subscriber
228
+
229
+ After adding tables to a publication, the subscriber must refresh:
230
+
231
+ ```sql
232
+ -- On the subscriber: pick up new tables and copy initial data
233
+ ALTER SUBSCRIPTION my_sub REFRESH PUBLICATION;
234
+
235
+ -- Refresh without copying data for new tables
236
+ ALTER SUBSCRIPTION my_sub REFRESH PUBLICATION WITH (copy_data = false);
237
+ ```
238
+
239
+ ### Drop a Publication
240
+
241
+ ```sql
242
+ DROP PUBLICATION my_pub;
243
+ -- or
244
+ DROP PUBLICATION IF EXISTS my_pub;
245
+ ```
246
+
247
+ ## Managing Subscriptions
248
+
249
+ ### Enable / Disable
250
+
251
+ ```sql
252
+ -- Pause replication
253
+ ALTER SUBSCRIPTION my_sub DISABLE;
254
+
255
+ -- Resume replication
256
+ ALTER SUBSCRIPTION my_sub ENABLE;
257
+ ```
258
+
259
+ ### Change Connection
260
+
261
+ ```sql
262
+ ALTER SUBSCRIPTION my_sub CONNECTION 'host=new_host dbname=source_db user=repl_user password=...';
263
+ ```
264
+
265
+ ### Change Publications
266
+
267
+ ```sql
268
+ -- Switch to different publications
269
+ ALTER SUBSCRIPTION my_sub SET PUBLICATION new_pub;
270
+
271
+ -- Add a publication
272
+ ALTER SUBSCRIPTION my_sub ADD PUBLICATION extra_pub;
273
+
274
+ -- Remove a publication
275
+ ALTER SUBSCRIPTION my_sub DROP PUBLICATION old_pub;
276
+ ```
277
+
278
+ ### Drop a Subscription
279
+
280
+ ```sql
281
+ -- This also drops the replication slot on the publisher
282
+ DROP SUBSCRIPTION my_sub;
283
+ ```
284
+
285
+ If the publisher is unreachable, disable first then drop:
286
+
287
+ ```sql
288
+ ALTER SUBSCRIPTION my_sub DISABLE;
289
+ ALTER SUBSCRIPTION my_sub SET (slot_name = NONE);
290
+ DROP SUBSCRIPTION my_sub;
291
+ -- Then manually drop the orphaned slot on the publisher when it's back:
292
+ -- SELECT pg_drop_replication_slot('my_sub');
293
+ ```
294
+
295
+ ## Monitoring Replication Progress
296
+
297
+ ### On the Publisher: Replication Slots and Lag
298
+
299
+ ```sql
300
+ -- Replication slot status
301
+ SELECT
302
+ slot_name,
303
+ plugin,
304
+ slot_type,
305
+ active,
306
+ pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS replication_lag
307
+ FROM pg_replication_slots
308
+ WHERE slot_type = 'logical';
309
+
310
+ -- Active WAL senders
311
+ SELECT
312
+ pid,
313
+ application_name,
314
+ client_addr,
315
+ state,
316
+ sent_lsn,
317
+ write_lsn,
318
+ flush_lsn,
319
+ replay_lsn,
320
+ pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS lag
321
+ FROM pg_stat_replication;
322
+ ```
323
+
324
+ ### On the Subscriber: Subscription Status
325
+
326
+ ```sql
327
+ -- Overall subscription status
328
+ SELECT
329
+ subname,
330
+ pid,
331
+ received_lsn,
332
+ latest_end_lsn,
333
+ latest_end_time,
334
+ last_msg_send_time,
335
+ last_msg_receipt_time
336
+ FROM pg_stat_subscription
337
+ WHERE subname IS NOT NULL;
338
+
339
+ -- Per-table sync state (initial copy progress)
340
+ SELECT
341
+ s.subname AS subscription,
342
+ sr.srrelid::regclass AS table_name,
343
+ sr.srsubstate AS state,
344
+ CASE sr.srsubstate
345
+ WHEN 'i' THEN 'initialize'
346
+ WHEN 'd' THEN 'data is being copied'
347
+ WHEN 'f' THEN 'finished table copy'
348
+ WHEN 's' THEN 'synchronized'
349
+ WHEN 'r' THEN 'ready (normal replication)'
350
+ ELSE 'unknown'
351
+ END AS state_meaning,
352
+ sr.srsublsn AS lsn
353
+ FROM pg_catalog.pg_subscription_rel AS sr
354
+ JOIN pg_catalog.pg_subscription AS s
355
+ ON s.oid = sr.srsubid
356
+ ORDER BY s.subname, sr.srrelid::regclass::text;
357
+ ```
358
+
359
+ **State codes for `pg_subscription_rel.srsubstate`:**
360
+
361
+ | Code | Meaning |
362
+ |------|---------|
363
+ | `i` | Initializing |
364
+ | `d` | Copying data (initial sync) |
365
+ | `f` | Finished table copy, waiting for sync |
366
+ | `s` | Synced with publisher |
367
+ | `r` | Ready (streaming) |
368
+
369
+ These codes describe each table's initialization state. `s` means the table synchronized during initialization; it does not prove that current replication lag is zero.
370
+
371
+ ### Replication Lag Monitoring Query
372
+
373
+ Run on the subscriber to check how far behind it is:
374
+
375
+ ```sql
376
+ -- Lag in bytes and time
377
+ SELECT
378
+ s.subname,
379
+ st.received_lsn,
380
+ st.latest_end_lsn,
381
+ st.latest_end_time,
382
+ now() - st.latest_end_time AS time_lag
383
+ FROM pg_subscription s
384
+ JOIN pg_stat_subscription st ON st.subid = s.oid
385
+ WHERE st.pid IS NOT NULL
386
+ AND st.relid IS NULL
387
+ -- PG17+ can also expose parallel apply workers; keep only the leader.
388
+ AND coalesce(to_jsonb(st)->>'worker_type', 'apply') = 'apply';
389
+ ```
390
+
391
+ ## Schema Changes During Replication
392
+
393
+ Logical replication does **NOT** replicate DDL. Schema changes must be applied manually on both sides.
394
+
395
+ ### Safe Pattern for Adding a Column
396
+
397
+ ```sql
398
+ -- 1. Add column on SUBSCRIBER first (nullable, no default)
399
+ ALTER TABLE orders ADD COLUMN priority int;
400
+
401
+ -- 2. Add column on PUBLISHER
402
+ ALTER TABLE orders ADD COLUMN priority int;
403
+
404
+ -- 3. New rows will now include the column
405
+ -- Existing replicated rows will have NULL for the new column
406
+ ```
407
+
408
+ **Add on subscriber first** to avoid errors when the publisher starts sending the new column before the subscriber schema is updated.
409
+
410
+ ### Safe Pattern for Dropping a Column
411
+
412
+ ```sql
413
+ -- 1. Remove column from PUBLICATION (PG15+, if using column lists)
414
+ ALTER PUBLICATION my_pub SET TABLE orders (id, customer_id, total, created_at);
415
+
416
+ -- 2. Drop column on PUBLISHER
417
+ ALTER TABLE orders DROP COLUMN old_column;
418
+
419
+ -- 3. Drop column on SUBSCRIBER
420
+ ALTER TABLE orders DROP COLUMN old_column;
421
+ ```
422
+
423
+ ### Adding a New Table to Replication
424
+
425
+ ```sql
426
+ -- 1. Create table on SUBSCRIBER with matching schema
427
+ CREATE TABLE shipments (...);
428
+
429
+ -- 2. Add table to publication on PUBLISHER
430
+ ALTER PUBLICATION my_pub ADD TABLE shipments;
431
+
432
+ -- 3. Refresh on SUBSCRIBER (copies existing data)
433
+ ALTER SUBSCRIPTION my_sub REFRESH PUBLICATION;
434
+ ```
435
+
436
+ ## Live Migration Patterns
437
+
438
+ ### Migration to a New Database (Minimal Downtime)
439
+
440
+ 1. **Set up target**: Create schema and constraints on the new database. **Defer index creation** until after initial data copy completes — this significantly speeds up the initial sync.
441
+ 2. **Start replication**: Create publication on source, subscription on target
442
+ 3. **Wait for sync**: Monitor until all tables reach `r` (ready) state
443
+ 4. **Verify**: Compare row counts, spot-check data
444
+ 5. **Cutover**:
445
+ - Stop writes to source (set `default_transaction_read_only = on` or revoke write access)
446
+ - Wait for final lag to drain to zero
447
+ - Verify sequences: advance sequences on target to match source
448
+ - Switch application connection strings
449
+ 6. **Cleanup**: Drop subscription, drop publication, drop replication slot
450
+
451
+ ### Sequence Synchronization
452
+
453
+ Logical replication does **NOT** replicate sequences. Before cutover, sync them:
454
+
455
+ ```sql
456
+ -- On SOURCE: get current sequence values
457
+ SELECT schemaname, sequencename, last_value
458
+ FROM pg_sequences
459
+ WHERE schemaname = 'public';
460
+
461
+ -- On TARGET: substitute the value obtained from the source.
462
+ -- Add a buffer only if writes can still reach the source during cutover.
463
+ SELECT pg_catalog.setval(
464
+ 'public.orders_id_seq'::regclass,
465
+ <source_last_value> + 1000,
466
+ true
467
+ );
468
+ ```
469
+
470
+ ### Row Count Verification
471
+
472
+ ```sql
473
+ -- Run on both source and target, compare results
474
+ SELECT
475
+ schemaname,
476
+ relname,
477
+ n_live_tup AS approx_row_count
478
+ FROM pg_stat_user_tables
479
+ WHERE schemaname = 'public'
480
+ ORDER BY relname;
481
+ ```
482
+
483
+ For exact counts (slower):
484
+
485
+ ```sql
486
+ CREATE TEMP TABLE exact_row_counts (
487
+ schemaname name NOT NULL,
488
+ tablename name NOT NULL,
489
+ exact_count bigint NOT NULL
490
+ );
491
+
492
+ DO $$
493
+ DECLARE r record;
494
+ BEGIN
495
+ FOR r IN
496
+ SELECT schemaname, tablename
497
+ FROM pg_catalog.pg_tables
498
+ WHERE schemaname = 'public'
499
+ ORDER BY tablename
500
+ LOOP
501
+ EXECUTE format(
502
+ 'INSERT INTO pg_temp.exact_row_counts
503
+ SELECT %L::name, %L::name, count(*)
504
+ FROM %I.%I',
505
+ r.schemaname, r.tablename,
506
+ r.schemaname, r.tablename
507
+ );
508
+ END LOOP;
509
+ END $$;
510
+
511
+ SELECT
512
+ format('%I.%I', schemaname, tablename) AS table_name,
513
+ exact_count
514
+ FROM pg_temp.exact_row_counts
515
+ ORDER BY schemaname, tablename;
516
+
517
+ DROP TABLE pg_temp.exact_row_counts;
518
+ ```
519
+
520
+ Run the exact-count query on both source and target only after stopping writes and allowing replication lag to drain. It performs a full scan of every selected table.
521
+
522
+ ## Troubleshooting
523
+
524
+ ### Replication Slot Growing / WAL Accumulation
525
+
526
+ If a subscriber falls behind or is disconnected, WAL accumulates on the publisher:
527
+
528
+ ```sql
529
+ -- Check slot lag on publisher
530
+ SELECT slot_name, active,
531
+ pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
532
+ FROM pg_replication_slots;
533
+ ```
534
+
535
+ **Fix**: Reconnect the subscriber, or drop the slot if the subscription is no longer needed:
536
+
537
+ ```sql
538
+ -- Drop an inactive slot (publisher)
539
+ SELECT pg_drop_replication_slot('orphaned_slot_name');
540
+ ```
541
+
542
+ ### Initial Sync Stuck or Slow
543
+
544
+ ```sql
545
+ -- Check which tables are still copying
546
+ SELECT srrelid::regclass AS table_name, srsubstate AS state
547
+ FROM pg_subscription_rel
548
+ WHERE srsubstate != 'r';
549
+ -- 'd' = still copying data, 'i' = initializing
550
+ ```
551
+
552
+ Initial sync speed depends on table size and network. For very large tables, consider:
553
+ - Dump/restore the data first, then create subscription with `copy_data = false`
554
+ - Increase `max_logical_replication_workers` if many tables need initial sync in parallel
555
+
556
+ ### Conflict Errors
557
+
558
+ If the subscriber has conflicting data (e.g., duplicate key):
559
+
560
+ ```sql
561
+ -- PG15+: cumulative subscription apply-error counters
562
+ SELECT * FROM pg_stat_subscription_stats;
563
+ ```
564
+
565
+ This view reports counters rather than detailed error messages. Check PostgreSQL server logs for the specific conflict; on PG14, logs are the primary source because `pg_stat_subscription_stats` is unavailable.
566
+
567
+ **PG16+**: Skip a conflicting transaction:
568
+
569
+ ```sql
570
+ ALTER SUBSCRIPTION my_sub SKIP (lsn = '0/12345678');
571
+ ```
572
+
573
+ **Pre-PG16**: Delete the conflicting row on the subscriber, then replication will proceed.
574
+
575
+ ### "Publisher Does Not Exist" After Refresh
576
+
577
+ If you renamed or recreated a publication:
578
+
579
+ ```sql
580
+ -- Subscriber needs to be pointed to the new publication name
581
+ ALTER SUBSCRIPTION my_sub SET PUBLICATION new_pub_name;
582
+ ALTER SUBSCRIPTION my_sub REFRESH PUBLICATION;
583
+ ```
584
+
585
+ ### Inactive Slot Timeout (PG18+)
586
+
587
+ PG18 adds `idle_replication_slot_timeout` to automatically invalidate inactive replication slots, preventing unbounded WAL accumulation:
588
+
589
+ ```sql
590
+ SHOW idle_replication_slot_timeout; -- auto-invalidates stale slots
591
+ ```
592
+
593
+ ### Generated Column Replication (PG18+)
594
+
595
+ Logical replication can now replicate generated column values via the `publish_generated_columns` publication option:
596
+
597
+ ```sql
598
+ CREATE PUBLICATION my_pub FOR TABLE orders
599
+ WITH (publish_generated_columns = true);
600
+ ```
601
+
602
+ ### Check if wal_level is Logical
603
+
604
+ ```sql
605
+ SHOW wal_level;
606
+ -- If not 'logical', it requires a restart to change
607
+ -- In postgresql.conf: wal_level = logical
608
+ ```
609
+
610
+ On managed platforms, `wal_level` may be controlled by the platform's settings UI or API rather than `postgresql.conf`.