@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,362 @@
1
+ # Schema Design Reference
2
+
3
+ ## Contents
4
+ - Data type best practices
5
+ - Primary keys
6
+ - Foreign keys and referential integrity
7
+ - Check and exclusion constraints
8
+ - Normalization guidelines
9
+ - Denormalization patterns
10
+ - Partitioning
11
+ - Multi-tenant patterns
12
+ - Migration safety
13
+
14
+ ## Data Type Best Practices
15
+
16
+ ### Identity Columns
17
+
18
+ ```sql
19
+ -- Preferred: GENERATED ALWAYS
20
+ CREATE TABLE orders (
21
+ id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
22
+ ...
23
+ );
24
+
25
+ -- If you need to occasionally override:
26
+ INSERT INTO orders OVERRIDING SYSTEM VALUE ...
27
+
28
+ -- UUID alternative (good for distributed systems)
29
+ CREATE TABLE orders (
30
+ id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
31
+ ...
32
+ );
33
+ ```
34
+
35
+ **UUID v4 vs v7**: `gen_random_uuid()` generates UUIDv4 (random), which causes B-tree index fragmentation on large tables because inserts scatter across the index. UUIDv7 (time-ordered) preserves insert locality. **PG18+** has built-in `uuidv7()` (and explicit `uuidv4()`). On older versions, use the `pg_uuidv7` extension. Prefer UUIDv7 for primary keys.
36
+
37
+ ```sql
38
+ -- PG18+: built-in UUIDv7
39
+ CREATE TABLE events (
40
+ id uuid DEFAULT uuidv7() PRIMARY KEY,
41
+ ...
42
+ );
43
+ ```
44
+
45
+ Avoid `serial` / `bigserial` — they create an implicit sequence with looser ownership semantics.
46
+
47
+ ### Timestamps
48
+
49
+ Always use `timestamptz`. PostgreSQL stores it as UTC internally and converts on display.
50
+
51
+ ```sql
52
+ CREATE TABLE events (
53
+ id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
54
+ occurred_at timestamptz NOT NULL DEFAULT now(),
55
+ -- NOT: occurred_at timestamp (loses timezone context)
56
+ );
57
+ ```
58
+
59
+ ### Text Fields
60
+
61
+ Prefer `text` with a `CHECK` constraint over `varchar(n)`:
62
+
63
+ ```sql
64
+ CREATE TABLE users (
65
+ email text NOT NULL CHECK (length(email) <= 254),
66
+ username text NOT NULL CHECK (length(username) BETWEEN 3 AND 30)
67
+ );
68
+ ```
69
+
70
+ `varchar(n)` provides no performance benefit and makes future changes harder.
71
+
72
+ ### UNIQUE NULLS NOT DISTINCT (PG15+)
73
+
74
+ By default, NULLs are considered distinct in unique constraints (multiple NULLs allowed). PG15 adds an option to treat NULLs as equal:
75
+
76
+ ```sql
77
+ -- Standard: allows multiple rows with NULL in email
78
+ CREATE UNIQUE INDEX idx_email_standard ON users(email);
79
+
80
+ -- PG15+: only one NULL allowed
81
+ CREATE UNIQUE INDEX idx_email_nulls_not_distinct
82
+ ON users(email) NULLS NOT DISTINCT;
83
+ ```
84
+
85
+ ### Naming Conventions
86
+
87
+ Use `snake_case` without quotes for all identifiers. Unquoted identifiers fold to lowercase automatically. Quoted mixed-case identifiers (e.g., `"userId"`) require quotes everywhere and break many ORMs, tools, and AI assistants.
88
+
89
+ ### Enums vs. Check Constraints vs. Lookup Tables
90
+
91
+ | Approach | Pros | Cons |
92
+ |----------|------|------|
93
+ | `CHECK (col IN (...))` | Simple, no DDL type | Requires ALTER TABLE to add values |
94
+ | `CREATE TYPE ... AS ENUM` | Type safety, compact storage | Cannot remove values, ALTER TYPE needed |
95
+ | Lookup/reference table | FK enforced, can add metadata | Extra join |
96
+
97
+ For small, stable sets (status, priority): CHECK or ENUM.
98
+ For evolving sets or sets needing metadata: lookup table.
99
+
100
+ ## Foreign Keys
101
+
102
+ ### Basics
103
+
104
+ ```sql
105
+ CREATE TABLE orders (
106
+ id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
107
+ customer_id bigint NOT NULL REFERENCES customers(id),
108
+ ...
109
+ );
110
+ ```
111
+
112
+ ### ON DELETE / ON UPDATE Actions
113
+
114
+ | Action | Use when |
115
+ |--------|----------|
116
+ | `NO ACTION` (default) | Reject the update or deletion if a violation remains when the constraint is checked; the check can be deferred when the foreign key is deferrable and currently deferred |
117
+ | `RESTRICT` | Reject the referenced operation immediately; the check cannot be deferred |
118
+ | `CASCADE` | Child rows are meaningless without parent (e.g., order_items when order is deleted) |
119
+ | `SET NULL` | Relationship is optional, preserve child row |
120
+ | `SET DEFAULT` | Rare; reassign to a valid default parent |
121
+
122
+ ### Deferrable Foreign Keys
123
+
124
+ Useful when you need to insert rows in both tables within a single transaction regardless of order:
125
+
126
+ ```sql
127
+ ALTER TABLE order_items
128
+ ADD CONSTRAINT fk_order
129
+ FOREIGN KEY (order_id) REFERENCES orders(id)
130
+ DEFERRABLE INITIALLY DEFERRED;
131
+ ```
132
+
133
+ ### Index the FK Column
134
+
135
+ PostgreSQL does NOT auto-create indexes on FK columns. Always add one:
136
+
137
+ ```sql
138
+ CREATE INDEX idx_orders_customer_id ON orders(customer_id);
139
+ ```
140
+
141
+ Without this index:
142
+ - JOINs on the FK are slow (seq scan on child table)
143
+ - DELETE on the parent table locks the child table and scans it fully
144
+
145
+ ## Check Constraints
146
+
147
+ ```sql
148
+ CREATE TABLE products (
149
+ price numeric(10,2) NOT NULL CHECK (price > 0),
150
+ discount_pct numeric(3,2) CHECK (discount_pct BETWEEN 0 AND 1),
151
+ start_date date NOT NULL,
152
+ end_date date,
153
+ CHECK (end_date IS NULL OR end_date > start_date)
154
+ );
155
+ ```
156
+
157
+ ### Exclusion Constraints (Prevent Overlaps)
158
+
159
+ ```sql
160
+ CREATE EXTENSION IF NOT EXISTS btree_gist;
161
+
162
+ CREATE TABLE room_bookings (
163
+ room_id int NOT NULL,
164
+ during tstzrange NOT NULL,
165
+ EXCLUDE USING gist (room_id WITH =, during WITH &&)
166
+ );
167
+ ```
168
+
169
+ This prevents overlapping bookings for the same room at the database level.
170
+
171
+ ### Temporal Constraints — WITHOUT OVERLAPS (PG18+)
172
+
173
+ PG18 adds built-in temporal constraint support, simplifying the exclusion constraint pattern above:
174
+
175
+ ```sql
176
+ CREATE EXTENSION IF NOT EXISTS btree_gist;
177
+
178
+ CREATE TABLE room_bookings (
179
+ room_id int NOT NULL,
180
+ during tstzrange NOT NULL,
181
+ PRIMARY KEY (room_id, during WITHOUT OVERLAPS)
182
+ );
183
+ ```
184
+
185
+ Foreign keys can also reference temporal primary keys using `PERIOD`:
186
+
187
+ ```sql
188
+ CREATE TABLE reservations (
189
+ id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
190
+ room_id int NOT NULL,
191
+ during tstzrange NOT NULL,
192
+ FOREIGN KEY (room_id, PERIOD during)
193
+ REFERENCES room_bookings (room_id, PERIOD during)
194
+ );
195
+ ```
196
+
197
+ This eliminates the need to write the exclusion constraint manually. The temporal key is implemented with GiST; scalar key columns such as `room_id int` still need an appropriate GiST operator class, supplied by `btree_gist` for common scalar types.
198
+
199
+ ### NOT ENFORCED Constraints (PG18+)
200
+
201
+ CHECK and foreign key constraints can be marked as informational only — the database trusts the data without enforcing the constraint. Useful for documenting intent or helping the planner without paying enforcement cost:
202
+
203
+ ```sql
204
+ ALTER TABLE orders ADD CONSTRAINT positive_total
205
+ CHECK (total > 0) NOT ENFORCED;
206
+ ```
207
+
208
+ ## Normalization Guidelines
209
+
210
+ ### When to Normalize
211
+
212
+ - Data integrity is critical (financial, compliance)
213
+ - Multiple writers update the same logical data
214
+ - Storage is a concern (avoid data duplication)
215
+
216
+ ### When to Denormalize
217
+
218
+ - Read-heavy workloads where join cost is measurable
219
+ - Materialized views can serve as denormalized read models
220
+ - JSONB columns for flexible, schema-less attributes that don't need FK integrity
221
+
222
+ ### Materialized Views as Denormalization
223
+
224
+ ```sql
225
+ CREATE MATERIALIZED VIEW mv_order_summary AS
226
+ SELECT
227
+ o.id,
228
+ o.created_at,
229
+ c.name AS customer_name,
230
+ sum(oi.quantity * oi.unit_price) AS total
231
+ FROM orders o
232
+ JOIN customers c ON c.id = o.customer_id
233
+ JOIN order_items oi ON oi.order_id = o.id
234
+ GROUP BY o.id, o.created_at, c.name;
235
+
236
+ CREATE UNIQUE INDEX ON mv_order_summary(id);
237
+
238
+ -- Refresh periodically or after batch writes:
239
+ REFRESH MATERIALIZED VIEW CONCURRENTLY mv_order_summary;
240
+ ```
241
+
242
+ ## Partitioning (Declarative)
243
+
244
+ ### When to Partition
245
+
246
+ - Table exceeds tens of millions of rows
247
+ - Queries consistently filter on the partition key
248
+ - You need efficient bulk deletion (DROP partition vs. DELETE)
249
+ - Maintenance operations (VACUUM, reindex) are slow on the full table
250
+
251
+ ### Range Partitioning (most common)
252
+
253
+ ```sql
254
+ CREATE TABLE events (
255
+ id bigint GENERATED ALWAYS AS IDENTITY,
256
+ occurred_at timestamptz NOT NULL,
257
+ payload jsonb
258
+ ) PARTITION BY RANGE (occurred_at);
259
+
260
+ CREATE TABLE events_2024_q1 PARTITION OF events
261
+ FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
262
+ CREATE TABLE events_2024_q2 PARTITION OF events
263
+ FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
264
+ ```
265
+
266
+ ### List Partitioning (multi-tenant)
267
+
268
+ ```sql
269
+ CREATE TABLE tenant_data (
270
+ tenant_id int NOT NULL,
271
+ data jsonb
272
+ ) PARTITION BY LIST (tenant_id);
273
+
274
+ CREATE TABLE tenant_data_1 PARTITION OF tenant_data FOR VALUES IN (1);
275
+ CREATE TABLE tenant_data_2 PARTITION OF tenant_data FOR VALUES IN (2);
276
+ ```
277
+
278
+ ### Detaching Partitions (PG14+)
279
+
280
+ ```sql
281
+ -- Non-blocking detach (PG14+): doesn't hold AccessExclusiveLock
282
+ ALTER TABLE events DETACH PARTITION events_2024_q1 CONCURRENTLY;
283
+ ```
284
+
285
+ ### Key Rules
286
+
287
+ - Partition key must be part of the primary key and all unique indexes
288
+ - Indexes defined on parent are auto-created on child partitions
289
+ - Foreign keys referencing partitioned tables are supported
290
+ - Exclusion constraints on partitioned tables require PG17+ (equality on partition key only)
291
+
292
+ ## Virtual Generated Columns (PG18+)
293
+
294
+ PG18 defaults generated columns to `VIRTUAL` (computed at read time, not stored on disk). Use `STORED` when you need to index the expression.
295
+
296
+ ```sql
297
+ CREATE TABLE orders (
298
+ id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
299
+ quantity int NOT NULL,
300
+ unit_price numeric(10,2) NOT NULL,
301
+ description text NOT NULL,
302
+ -- Virtual: computed on read, no storage cost
303
+ total numeric GENERATED ALWAYS AS (quantity * unit_price) VIRTUAL,
304
+ -- Stored: persisted on disk, can be indexed
305
+ search_text tsvector GENERATED ALWAYS AS (
306
+ to_tsvector('english', description)
307
+ ) STORED
308
+ );
309
+ ```
310
+
311
+ Virtual columns save storage and write I/O but cannot be indexed directly. Use `STORED` when you need an index on the expression.
312
+
313
+ ## Multi-Tenant Patterns
314
+
315
+ ### Row-Level Security (RLS)
316
+
317
+ ```sql
318
+ ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
319
+
320
+ CREATE POLICY tenant_isolation ON orders
321
+ USING (tenant_id = current_setting('app.current_tenant')::int);
322
+
323
+ -- Set per-connection:
324
+ SET app.current_tenant = '42';
325
+ ```
326
+
327
+ ### Shared Schema with tenant_id
328
+
329
+ - Add `tenant_id` to every table
330
+ - Include `tenant_id` in all indexes (composite with other columns)
331
+ - Use RLS or application-level filtering
332
+
333
+ ## Migration Safety
334
+
335
+ ### Safe Column Operations
336
+
337
+ | Operation | Safe online? | Notes |
338
+ |-----------|-------------|-------|
339
+ | `ADD COLUMN` (nullable, no default) | Yes | Instant metadata change |
340
+ | `ADD COLUMN ... DEFAULT x` | Yes | Default stored in catalog, no table rewrite |
341
+ | `DROP COLUMN` | Yes | Marks column as dropped, no rewrite |
342
+ | `ALTER COLUMN SET NOT NULL` | Caution | Full table scan to validate (use CHECK first) |
343
+ | `ALTER COLUMN TYPE` | No | Full table rewrite + exclusive lock |
344
+ | `ADD CONSTRAINT ... NOT VALID` | Yes | Doesn't scan existing rows |
345
+ | `VALIDATE CONSTRAINT` | Yes | ShareUpdateExclusiveLock (reads/writes allowed) |
346
+
347
+ ### Safe NOT NULL Pattern
348
+
349
+ ```sql
350
+ -- Step 1: Add CHECK constraint without validating existing rows
351
+ ALTER TABLE orders ADD CONSTRAINT orders_status_nn
352
+ CHECK (status IS NOT NULL) NOT VALID;
353
+
354
+ -- Step 2: Validate in background (non-blocking)
355
+ ALTER TABLE orders VALIDATE CONSTRAINT orders_status_nn;
356
+
357
+ -- Step 3: SET NOT NULL is instant if a valid CHECK exists
358
+ ALTER TABLE orders ALTER COLUMN status SET NOT NULL;
359
+
360
+ -- Step 4: Drop the now-redundant CHECK
361
+ ALTER TABLE orders DROP CONSTRAINT orders_status_nn;
362
+ ```