tribunal-kit 3.0.0 → 3.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.
- package/.agent/ARCHITECTURE.md +99 -99
- package/.agent/GEMINI.md +52 -52
- package/.agent/agents/accessibility-reviewer.md +187 -220
- package/.agent/agents/ai-code-reviewer.md +199 -233
- package/.agent/agents/backend-specialist.md +215 -238
- package/.agent/agents/code-archaeologist.md +161 -181
- package/.agent/agents/database-architect.md +184 -207
- package/.agent/agents/debugger.md +191 -218
- package/.agent/agents/dependency-reviewer.md +103 -136
- package/.agent/agents/devops-engineer.md +218 -238
- package/.agent/agents/documentation-writer.md +201 -221
- package/.agent/agents/explorer-agent.md +160 -180
- package/.agent/agents/frontend-reviewer.md +160 -194
- package/.agent/agents/frontend-specialist.md +248 -237
- package/.agent/agents/game-developer.md +48 -52
- package/.agent/agents/logic-reviewer.md +116 -149
- package/.agent/agents/mobile-developer.md +200 -223
- package/.agent/agents/mobile-reviewer.md +162 -195
- package/.agent/agents/orchestrator.md +181 -211
- package/.agent/agents/penetration-tester.md +157 -174
- package/.agent/agents/performance-optimizer.md +183 -203
- package/.agent/agents/performance-reviewer.md +178 -211
- package/.agent/agents/product-manager.md +142 -162
- package/.agent/agents/product-owner.md +6 -25
- package/.agent/agents/project-planner.md +142 -162
- package/.agent/agents/qa-automation-engineer.md +225 -242
- package/.agent/agents/security-auditor.md +174 -194
- package/.agent/agents/seo-specialist.md +193 -213
- package/.agent/agents/sql-reviewer.md +161 -194
- package/.agent/agents/supervisor-agent.md +184 -203
- package/.agent/agents/swarm-worker-contracts.md +17 -17
- package/.agent/agents/swarm-worker-registry.md +46 -46
- package/.agent/agents/test-coverage-reviewer.md +160 -193
- package/.agent/agents/test-engineer.md +0 -21
- package/.agent/agents/type-safety-reviewer.md +175 -208
- package/.agent/patterns/generator.md +9 -9
- package/.agent/patterns/inversion.md +12 -12
- package/.agent/patterns/pipeline.md +9 -9
- package/.agent/patterns/reviewer.md +13 -13
- package/.agent/patterns/tool-wrapper.md +9 -9
- package/.agent/rules/GEMINI.md +63 -63
- package/.agent/scripts/compress_skills.py +167 -0
- package/.agent/scripts/consolidate_skills.py +173 -0
- package/.agent/scripts/deep_compress.py +202 -0
- package/.agent/scripts/minify_context.py +80 -0
- package/.agent/scripts/security_scan.py +1 -1
- package/.agent/scripts/strip_tribunal.py +41 -0
- package/.agent/skills/agent-organizer/SKILL.md +92 -126
- package/.agent/skills/agentic-patterns/SKILL.md +0 -70
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +126 -160
- package/.agent/skills/api-patterns/SKILL.md +123 -215
- package/.agent/skills/api-security-auditor/SKILL.md +143 -177
- package/.agent/skills/app-builder/SKILL.md +326 -50
- package/.agent/skills/app-builder/templates/SKILL.md +13 -15
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +16 -16
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +22 -22
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +20 -20
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +17 -17
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +21 -21
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +19 -19
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +26 -26
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +26 -26
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +19 -19
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +20 -20
- package/.agent/skills/appflow-wireframe/SKILL.md +87 -121
- package/.agent/skills/architecture/SKILL.md +82 -252
- package/.agent/skills/authentication-best-practices/SKILL.md +139 -173
- package/.agent/skills/bash-linux/SKILL.md +120 -154
- package/.agent/skills/behavioral-modes/SKILL.md +8 -69
- package/.agent/skills/brainstorming/SKILL.md +428 -104
- package/.agent/skills/building-native-ui/SKILL.md +143 -174
- package/.agent/skills/clean-code/SKILL.md +323 -360
- package/.agent/skills/code-review-checklist/SKILL.md +0 -62
- package/.agent/skills/config-validator/SKILL.md +107 -141
- package/.agent/skills/csharp-developer/SKILL.md +468 -528
- package/.agent/skills/database-design/SKILL.md +104 -369
- package/.agent/skills/deployment-procedures/SKILL.md +111 -145
- package/.agent/skills/devops-engineer/SKILL.md +295 -332
- package/.agent/skills/devops-incident-responder/SKILL.md +79 -113
- package/.agent/skills/doc.md +5 -5
- package/.agent/skills/documentation-templates/SKILL.md +19 -63
- package/.agent/skills/edge-computing/SKILL.md +123 -157
- package/.agent/skills/extract-design-system/SKILL.md +100 -134
- package/.agent/skills/framer-motion-expert/SKILL.md +111 -855
- package/.agent/skills/frontend-design/SKILL.md +151 -499
- package/.agent/skills/game-design-expert/SKILL.md +71 -105
- package/.agent/skills/game-engineering-expert/SKILL.md +88 -122
- package/.agent/skills/geo-fundamentals/SKILL.md +89 -124
- package/.agent/skills/github-operations/SKILL.md +279 -314
- package/.agent/skills/gsap-expert/SKILL.md +119 -826
- package/.agent/skills/i18n-localization/SKILL.md +104 -138
- package/.agent/skills/intelligent-routing/SKILL.md +159 -127
- package/.agent/skills/lint-and-validate/SKILL.md +8 -52
- package/.agent/skills/llm-engineering/SKILL.md +344 -357
- package/.agent/skills/local-first/SKILL.md +120 -154
- package/.agent/skills/mcp-builder/SKILL.md +84 -118
- package/.agent/skills/mobile-design/SKILL.md +213 -219
- package/.agent/skills/motion-engineering/SKILL.md +184 -0
- package/.agent/skills/nextjs-react-expert/SKILL.md +99 -698
- package/.agent/skills/nodejs-best-practices/SKILL.md +498 -559
- package/.agent/skills/observability/SKILL.md +293 -330
- package/.agent/skills/parallel-agents/SKILL.md +88 -122
- package/.agent/skills/performance-profiling/SKILL.md +217 -254
- package/.agent/skills/plan-writing/SKILL.md +84 -118
- package/.agent/skills/platform-engineer/SKILL.md +89 -123
- package/.agent/skills/playwright-best-practices/SKILL.md +128 -162
- package/.agent/skills/powershell-windows/SKILL.md +112 -146
- package/.agent/skills/python-patterns/SKILL.md +7 -35
- package/.agent/skills/python-pro/SKILL.md +148 -754
- package/.agent/skills/react-specialist/SKILL.md +123 -827
- package/.agent/skills/readme-builder/SKILL.md +15 -85
- package/.agent/skills/realtime-patterns/SKILL.md +269 -304
- package/.agent/skills/red-team-tactics/SKILL.md +10 -51
- package/.agent/skills/rust-pro/SKILL.md +623 -701
- package/.agent/skills/seo-fundamentals/SKILL.md +120 -154
- package/.agent/skills/server-management/SKILL.md +156 -190
- package/.agent/skills/shadcn-ui-expert/SKILL.md +172 -206
- package/.agent/skills/skill-creator/SKILL.md +18 -58
- package/.agent/skills/sql-pro/SKILL.md +579 -633
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +28 -68
- package/.agent/skills/swiftui-expert/SKILL.md +142 -176
- package/.agent/skills/systematic-debugging/SKILL.md +84 -118
- package/.agent/skills/tailwind-patterns/SKILL.md +516 -576
- package/.agent/skills/tdd-workflow/SKILL.md +103 -137
- package/.agent/skills/test-result-analyzer/SKILL.md +33 -73
- package/.agent/skills/testing-patterns/SKILL.md +512 -573
- package/.agent/skills/trend-researcher/SKILL.md +30 -71
- package/.agent/skills/ui-ux-pro-max/SKILL.md +0 -41
- package/.agent/skills/ui-ux-researcher/SKILL.md +51 -91
- package/.agent/skills/vue-expert/SKILL.md +127 -866
- package/.agent/skills/vulnerability-scanner/SKILL.md +354 -269
- package/.agent/skills/web-accessibility-auditor/SKILL.md +159 -193
- package/.agent/skills/web-design-guidelines/SKILL.md +17 -61
- package/.agent/skills/webapp-testing/SKILL.md +111 -145
- package/.agent/skills/whimsy-injector/SKILL.md +58 -132
- package/.agent/skills/workflow-optimizer/SKILL.md +28 -68
- package/.agent/workflows/api-tester.md +151 -151
- package/.agent/workflows/audit.md +127 -138
- package/.agent/workflows/brainstorm.md +110 -110
- package/.agent/workflows/changelog.md +112 -112
- package/.agent/workflows/create.md +124 -124
- package/.agent/workflows/debug.md +165 -189
- package/.agent/workflows/deploy.md +180 -189
- package/.agent/workflows/enhance.md +128 -151
- package/.agent/workflows/fix.md +114 -135
- package/.agent/workflows/generate.md +12 -4
- package/.agent/workflows/migrate.md +160 -160
- package/.agent/workflows/orchestrate.md +168 -168
- package/.agent/workflows/performance-benchmarker.md +114 -123
- package/.agent/workflows/plan.md +173 -173
- package/.agent/workflows/preview.md +80 -80
- package/.agent/workflows/refactor.md +161 -183
- package/.agent/workflows/review-ai.md +101 -129
- package/.agent/workflows/review.md +116 -116
- package/.agent/workflows/session.md +94 -94
- package/.agent/workflows/status.md +79 -79
- package/.agent/workflows/strengthen-skills.md +138 -139
- package/.agent/workflows/swarm.md +179 -179
- package/.agent/workflows/test.md +189 -211
- package/.agent/workflows/tribunal-backend.md +93 -113
- package/.agent/workflows/tribunal-database.md +94 -115
- package/.agent/workflows/tribunal-frontend.md +95 -118
- package/.agent/workflows/tribunal-full.md +92 -133
- package/.agent/workflows/tribunal-mobile.md +94 -119
- package/.agent/workflows/tribunal-performance.md +109 -133
- package/.agent/workflows/ui-ux-pro-max.md +122 -143
- package/package.json +1 -1
- package/.agent/skills/api-patterns/api-style.md +0 -42
- package/.agent/skills/api-patterns/auth.md +0 -24
- package/.agent/skills/api-patterns/documentation.md +0 -26
- package/.agent/skills/api-patterns/graphql.md +0 -41
- package/.agent/skills/api-patterns/rate-limiting.md +0 -31
- package/.agent/skills/api-patterns/response.md +0 -37
- package/.agent/skills/api-patterns/rest.md +0 -40
- package/.agent/skills/api-patterns/security-testing.md +0 -122
- package/.agent/skills/api-patterns/trpc.md +0 -41
- package/.agent/skills/api-patterns/versioning.md +0 -22
- package/.agent/skills/app-builder/agent-coordination.md +0 -71
- package/.agent/skills/app-builder/feature-building.md +0 -53
- package/.agent/skills/app-builder/project-detection.md +0 -34
- package/.agent/skills/app-builder/scaffolding.md +0 -118
- package/.agent/skills/app-builder/tech-stack.md +0 -40
- package/.agent/skills/architecture/context-discovery.md +0 -43
- package/.agent/skills/architecture/examples.md +0 -94
- package/.agent/skills/architecture/pattern-selection.md +0 -68
- package/.agent/skills/architecture/patterns-reference.md +0 -50
- package/.agent/skills/architecture/trade-off-analysis.md +0 -77
- package/.agent/skills/brainstorming/dynamic-questioning.md +0 -360
- package/.agent/skills/database-design/database-selection.md +0 -43
- package/.agent/skills/database-design/indexing.md +0 -39
- package/.agent/skills/database-design/migrations.md +0 -48
- package/.agent/skills/database-design/optimization.md +0 -36
- package/.agent/skills/database-design/orm-selection.md +0 -30
- package/.agent/skills/database-design/schema-design.md +0 -56
- package/.agent/skills/frontend-design/animation-guide.md +0 -331
- package/.agent/skills/frontend-design/color-system.md +0 -329
- package/.agent/skills/frontend-design/decision-trees.md +0 -418
- package/.agent/skills/frontend-design/motion-graphics.md +0 -306
- package/.agent/skills/frontend-design/typography-system.md +0 -363
- package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
- package/.agent/skills/frontend-design/visual-effects.md +0 -383
- package/.agent/skills/intelligent-routing/router-manifest.md +0 -65
- package/.agent/skills/mobile-design/decision-trees.md +0 -516
- package/.agent/skills/mobile-design/mobile-backend.md +0 -491
- package/.agent/skills/mobile-design/mobile-color-system.md +0 -420
- package/.agent/skills/mobile-design/mobile-debugging.md +0 -122
- package/.agent/skills/mobile-design/mobile-design-thinking.md +0 -357
- package/.agent/skills/mobile-design/mobile-navigation.md +0 -458
- package/.agent/skills/mobile-design/mobile-performance.md +0 -767
- package/.agent/skills/mobile-design/mobile-testing.md +0 -356
- package/.agent/skills/mobile-design/mobile-typography.md +0 -433
- package/.agent/skills/mobile-design/platform-android.md +0 -666
- package/.agent/skills/mobile-design/platform-ios.md +0 -561
- package/.agent/skills/mobile-design/touch-psychology.md +0 -537
- package/.agent/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +0 -312
- package/.agent/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +0 -240
- package/.agent/skills/nextjs-react-expert/3-server-server-side-performance.md +0 -490
- package/.agent/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +0 -264
- package/.agent/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +0 -581
- package/.agent/skills/nextjs-react-expert/6-rendering-rendering-performance.md +0 -432
- package/.agent/skills/nextjs-react-expert/7-js-javascript-performance.md +0 -684
- package/.agent/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +0 -150
- package/.agent/skills/vulnerability-scanner/checklists.md +0 -121
|
@@ -2,454 +2,189 @@
|
|
|
2
2
|
name: database-design
|
|
3
3
|
description: Database design mastery. Schema design with normalization, denormalization strategies, indexing, migration pipelines, ORM selection (Prisma/Drizzle/SQLAlchemy/EF Core), connection pooling, soft deletes, audit trails, multi-tenancy, and serverless database patterns. Use when designing schemas, choosing databases, planning migrations, or architecting data layers.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Glob, Grep
|
|
5
|
-
version:
|
|
6
|
-
last-updated: 2026-04-
|
|
7
|
-
applies-to-model: gemini-
|
|
5
|
+
version: 3.1.0
|
|
6
|
+
last-updated: 2026-04-07
|
|
7
|
+
applies-to-model: gemini-3-1-pro, claude-3-7-sonnet
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Database Design — Schema & Architecture Mastery
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
## Hallucination Traps (Read First)
|
|
13
|
+
- ❌ `TIMESTAMP` → ✅ Always `TIMESTAMPTZ` (with timezone). `TIMESTAMP` is ambiguous across timezones.
|
|
14
|
+
- ❌ UUID v4 as primary key → ✅ UUID v7 (time-ordered) or `BIGINT GENERATED ALWAYS AS IDENTITY`. UUID v4 is random — destroys B-tree index performance on high-insert tables.
|
|
15
|
+
- ❌ No index on foreign keys → ✅ PostgreSQL does NOT auto-index FK columns. Cascading deletes cause full table scans without them.
|
|
16
|
+
- ❌ Adding `NOT NULL` column directly to a large table → ✅ Locks the entire table. Add as nullable, backfill in batches, then add constraint.
|
|
17
|
+
- ❌ Soft delete without a partial index → ✅ Every query must filter `WHERE deleted_at IS NULL`. Add `CREATE INDEX ... WHERE deleted_at IS NULL` or use a view.
|
|
18
|
+
- ❌ Serverless functions without a connection pooler → ✅ Each Lambda/Vercel invocation opens a new connection. Use PgBouncer or Supabase Supavisor — without it, you'll hit `max_connections` instantly.
|
|
14
19
|
|
|
15
20
|
---
|
|
16
21
|
|
|
17
|
-
## Database Selection
|
|
22
|
+
## Database Selection
|
|
18
23
|
|
|
19
24
|
```
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
│ │ ├── In-memory speed → Redis / Valkey │
|
|
32
|
-
│ │ └── Persistent KV → DynamoDB / Upstash │
|
|
33
|
-
│ │ │
|
|
34
|
-
│ ├── Document store (flexible schema, nested objects) │
|
|
35
|
-
│ │ └── MongoDB / Firestore │
|
|
36
|
-
│ │ │
|
|
37
|
-
│ ├── Full-text search │
|
|
38
|
-
│ │ ├── Built-in (good enough) → PostgreSQL tsvector │
|
|
39
|
-
│ │ └── Dedicated search → Elasticsearch / Meilisearch / Typesense│
|
|
40
|
-
│ │ │
|
|
41
|
-
│ ├── Time-series (metrics, IoT, logs) │
|
|
42
|
-
│ │ └── TimescaleDB (PostgreSQL ext) / ClickHouse / InfluxDB │
|
|
43
|
-
│ │ │
|
|
44
|
-
│ ├── Graph (relationships, social networks) │
|
|
45
|
-
│ │ └── Neo4j / Amazon Neptune │
|
|
46
|
-
│ │ │
|
|
47
|
-
│ └── Vector (AI embeddings, semantic search) │
|
|
48
|
-
│ └── pgvector (PostgreSQL ext) / Pinecone / Weaviate │
|
|
49
|
-
└─────────────────────────────────────────────────────────────────────┘
|
|
25
|
+
Relational / Complex queries → PostgreSQL (primary choice)
|
|
26
|
+
Serverless PG → Neon, Supabase
|
|
27
|
+
Edge / Ultra-low latency → Turso (SQLite @ edge)
|
|
28
|
+
Simple / Embedded → SQLite
|
|
29
|
+
Global distribution (MySQL) → PlanetScale (no FK support)
|
|
30
|
+
|
|
31
|
+
Key-value / Cache → Redis / Valkey / Upstash
|
|
32
|
+
Document store → MongoDB / Firestore
|
|
33
|
+
Full-text search → PostgreSQL tsvector (built-in) or Meilisearch / Typesense
|
|
34
|
+
Time-series → TimescaleDB / ClickHouse
|
|
35
|
+
Vector (AI embeddings) → pgvector (PostgreSQL ext) / Pinecone / Weaviate
|
|
50
36
|
```
|
|
51
37
|
|
|
52
38
|
---
|
|
53
39
|
|
|
54
|
-
##
|
|
55
|
-
|
|
56
|
-
### Naming Conventions
|
|
57
|
-
|
|
58
|
-
```sql
|
|
59
|
-
-- ✅ RULES:
|
|
60
|
-
-- Tables: plural, snake_case (users, order_items)
|
|
61
|
-
-- Columns: singular, snake_case (first_name, created_at)
|
|
62
|
-
-- Primary key: id (BIGINT or UUID)
|
|
63
|
-
-- Foreign key: {referenced_table_singular}_id (user_id, order_id)
|
|
64
|
-
-- Timestamps: created_at, updated_at (TIMESTAMPTZ, not TIMESTAMP)
|
|
65
|
-
-- Booleans: is_{adjective} or has_{noun} (is_active, has_paid)
|
|
66
|
-
-- Status/enum columns: status, role, type (not state, kind)
|
|
67
|
-
|
|
68
|
-
-- ❌ BAD naming:
|
|
69
|
-
-- tbl_Users, UserID, DateCreated, active, isdeleted, userId
|
|
70
|
-
-- ✅ GOOD naming:
|
|
71
|
-
-- users, id, created_at, is_active, is_deleted, user_id
|
|
72
|
-
|
|
73
|
-
-- ❌ HALLUCINATION TRAP: Always use TIMESTAMPTZ (with timezone), not TIMESTAMP
|
|
74
|
-
-- TIMESTAMP without timezone is ambiguous and causes bugs across timezones
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Standard Table Template
|
|
40
|
+
## Standard Table Template
|
|
78
41
|
|
|
79
42
|
```sql
|
|
80
43
|
CREATE TABLE users (
|
|
81
44
|
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
82
|
-
-- OR: id UUID DEFAULT gen_random_uuid() PRIMARY KEY
|
|
83
|
-
|
|
84
|
-
-- Core fields
|
|
45
|
+
-- OR: id UUID DEFAULT gen_random_uuid() PRIMARY KEY (use v7 for perf)
|
|
85
46
|
email TEXT NOT NULL UNIQUE,
|
|
86
47
|
name TEXT NOT NULL,
|
|
87
48
|
role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'user', 'moderator')),
|
|
88
|
-
|
|
89
|
-
-- Status fields
|
|
90
49
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
91
|
-
|
|
92
|
-
-- Metadata
|
|
93
50
|
metadata JSONB DEFAULT '{}',
|
|
94
|
-
|
|
95
|
-
-- Timestamps (ALWAYS include these)
|
|
96
51
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
97
|
-
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
52
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
53
|
+
deleted_at TIMESTAMPTZ -- soft delete
|
|
98
54
|
);
|
|
99
55
|
|
|
100
|
-
--
|
|
101
|
-
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
END;
|
|
107
|
-
$$ LANGUAGE plpgsql;
|
|
108
|
-
|
|
109
|
-
CREATE TRIGGER trg_users_updated_at
|
|
110
|
-
BEFORE UPDATE ON users
|
|
111
|
-
FOR EACH ROW
|
|
112
|
-
EXECUTE FUNCTION update_updated_at();
|
|
113
|
-
|
|
114
|
-
-- Essential indexes
|
|
56
|
+
-- Required: auto-update updated_at
|
|
57
|
+
CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$
|
|
58
|
+
BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ LANGUAGE plpgsql;
|
|
59
|
+
CREATE TRIGGER trg_users_updated_at BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
60
|
+
|
|
61
|
+
-- Required indexes
|
|
115
62
|
CREATE INDEX idx_users_email ON users (email);
|
|
116
|
-
CREATE INDEX
|
|
63
|
+
CREATE INDEX idx_users_active ON users (email) WHERE deleted_at IS NULL; -- partial index for soft delete
|
|
117
64
|
CREATE INDEX idx_users_created_at ON users (created_at DESC);
|
|
118
65
|
```
|
|
119
66
|
|
|
120
|
-
|
|
67
|
+
---
|
|
121
68
|
|
|
122
|
-
|
|
123
|
-
-- Option 1: BIGINT auto-increment (recommended for most cases)
|
|
124
|
-
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
|
|
125
|
-
-- Pros: compact, sortable, fast joins, natural ordering
|
|
126
|
-
-- Cons: exposes record count, sequential guessing
|
|
127
|
-
|
|
128
|
-
-- Option 2: UUID v7 (recommended for distributed systems)
|
|
129
|
-
id UUID DEFAULT gen_random_uuid() PRIMARY KEY
|
|
130
|
-
-- Pros: globally unique, no coordination needed, safe to expose
|
|
131
|
-
-- Cons: larger (16 bytes), slower joins, random order (use UUIDv7 for time-ordering)
|
|
132
|
-
|
|
133
|
-
-- Option 3: ULID / NanoID (application-generated)
|
|
134
|
-
-- Pros: sortable, URL-safe, customizable length
|
|
135
|
-
-- Cons: requires application logic, not DB-native
|
|
136
|
-
|
|
137
|
-
-- ❌ HALLUCINATION TRAP: UUID v4 is randomly ordered — kills index performance
|
|
138
|
-
-- ✅ Use UUID v7 (time-ordered) if you need UUIDs
|
|
139
|
-
-- UUID v7 has a timestamp prefix → sequential inserts → B-tree friendly
|
|
140
|
-
```
|
|
69
|
+
## Schema Patterns
|
|
141
70
|
|
|
142
71
|
### Relationships
|
|
143
|
-
|
|
144
72
|
```sql
|
|
145
|
-
-- One-to-Many:
|
|
73
|
+
-- One-to-Many: FK on the "many" side + INDEX
|
|
146
74
|
CREATE TABLE posts (
|
|
147
|
-
id
|
|
148
|
-
author_id
|
|
149
|
-
|
|
150
|
-
body TEXT NOT NULL,
|
|
151
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
75
|
+
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
76
|
+
author_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
77
|
+
...
|
|
152
78
|
);
|
|
153
|
-
CREATE INDEX idx_posts_author_id ON posts (author_id);
|
|
79
|
+
CREATE INDEX idx_posts_author_id ON posts (author_id); -- REQUIRED in Postgres
|
|
154
80
|
|
|
155
|
-
-- Many-to-Many: junction table
|
|
81
|
+
-- Many-to-Many: junction table with composite PK
|
|
156
82
|
CREATE TABLE post_tags (
|
|
157
|
-
post_id
|
|
158
|
-
tag_id
|
|
159
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
83
|
+
post_id BIGINT NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
|
|
84
|
+
tag_id BIGINT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
|
160
85
|
PRIMARY KEY (post_id, tag_id)
|
|
161
86
|
);
|
|
162
|
-
CREATE INDEX idx_post_tags_tag_id ON post_tags (tag_id);
|
|
163
|
-
|
|
164
|
-
-- ❌ HALLUCINATION TRAP: Every foreign key column MUST have an index
|
|
165
|
-
-- Without it, cascading deletes on the parent do a full table scan on the child
|
|
166
|
-
-- PostgreSQL does NOT auto-index foreign keys (MySQL InnoDB does)
|
|
167
|
-
|
|
168
|
-
-- Self-referential (tree/hierarchy)
|
|
169
|
-
CREATE TABLE categories (
|
|
170
|
-
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
171
|
-
parent_id BIGINT REFERENCES categories(id) ON DELETE SET NULL,
|
|
172
|
-
name TEXT NOT NULL,
|
|
173
|
-
depth INT NOT NULL DEFAULT 0
|
|
174
|
-
);
|
|
175
|
-
CREATE INDEX idx_categories_parent_id ON categories (parent_id);
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## Soft Deletes
|
|
181
|
-
|
|
182
|
-
```sql
|
|
183
|
-
-- Soft delete pattern
|
|
184
|
-
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMPTZ;
|
|
185
|
-
|
|
186
|
-
-- Partial index — queries against active records are fast
|
|
187
|
-
CREATE INDEX idx_users_active ON users (email) WHERE deleted_at IS NULL;
|
|
188
|
-
|
|
189
|
-
-- "Delete" = set timestamp
|
|
190
|
-
UPDATE users SET deleted_at = now() WHERE id = 42;
|
|
191
|
-
|
|
192
|
-
-- All queries must filter:
|
|
193
|
-
SELECT * FROM users WHERE deleted_at IS NULL;
|
|
194
|
-
|
|
195
|
-
-- OR: Use a view for convenience
|
|
196
|
-
CREATE VIEW active_users AS
|
|
197
|
-
SELECT * FROM users WHERE deleted_at IS NULL;
|
|
198
|
-
|
|
199
|
-
-- ❌ HALLUCINATION TRAP: Soft deletes add complexity to EVERY query
|
|
200
|
-
-- Every SELECT, JOIN, and COUNT must include WHERE deleted_at IS NULL
|
|
201
|
-
-- Consider using a view or audit_log table instead when possible
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
---
|
|
205
|
-
|
|
206
|
-
## Audit Trail
|
|
207
|
-
|
|
208
|
-
```sql
|
|
209
|
-
-- Append-only audit log
|
|
210
|
-
CREATE TABLE audit_log (
|
|
211
|
-
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
212
|
-
table_name TEXT NOT NULL,
|
|
213
|
-
record_id BIGINT NOT NULL,
|
|
214
|
-
action TEXT NOT NULL CHECK (action IN ('INSERT', 'UPDATE', 'DELETE')),
|
|
215
|
-
old_data JSONB,
|
|
216
|
-
new_data JSONB,
|
|
217
|
-
changed_by BIGINT REFERENCES users(id),
|
|
218
|
-
changed_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
-- Partition by month for performance
|
|
222
|
-
-- CREATE TABLE audit_log (...) PARTITION BY RANGE (changed_at);
|
|
223
|
-
|
|
224
|
-
CREATE INDEX idx_audit_log_table_record ON audit_log (table_name, record_id);
|
|
225
|
-
CREATE INDEX idx_audit_log_changed_at ON audit_log USING brin (changed_at);
|
|
226
|
-
|
|
227
|
-
-- Auto-audit trigger
|
|
228
|
-
CREATE OR REPLACE FUNCTION audit_trigger()
|
|
229
|
-
RETURNS TRIGGER AS $$
|
|
230
|
-
BEGIN
|
|
231
|
-
INSERT INTO audit_log (table_name, record_id, action, old_data, new_data, changed_by)
|
|
232
|
-
VALUES (
|
|
233
|
-
TG_TABLE_NAME,
|
|
234
|
-
COALESCE(NEW.id, OLD.id),
|
|
235
|
-
TG_OP,
|
|
236
|
-
CASE WHEN TG_OP IN ('UPDATE', 'DELETE') THEN to_jsonb(OLD) END,
|
|
237
|
-
CASE WHEN TG_OP IN ('INSERT', 'UPDATE') THEN to_jsonb(NEW) END,
|
|
238
|
-
current_setting('app.current_user_id', true)::bigint
|
|
239
|
-
);
|
|
240
|
-
RETURN COALESCE(NEW, OLD);
|
|
241
|
-
END;
|
|
242
|
-
$$ LANGUAGE plpgsql;
|
|
243
|
-
|
|
244
|
-
CREATE TRIGGER trg_users_audit
|
|
245
|
-
AFTER INSERT OR UPDATE OR DELETE ON users
|
|
246
|
-
FOR EACH ROW EXECUTE FUNCTION audit_trigger();
|
|
87
|
+
CREATE INDEX idx_post_tags_tag_id ON post_tags (tag_id); -- index the non-PK side
|
|
247
88
|
```
|
|
248
89
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
## Multi-Tenancy Patterns
|
|
252
|
-
|
|
90
|
+
### Multi-Tenancy
|
|
253
91
|
```sql
|
|
254
|
-
-- Pattern 1:
|
|
255
|
-
CREATE TABLE projects (
|
|
256
|
-
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
257
|
-
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
|
|
258
|
-
name TEXT NOT NULL,
|
|
259
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
260
|
-
);
|
|
261
|
-
CREATE INDEX idx_projects_tenant ON projects (tenant_id, created_at DESC);
|
|
262
|
-
-- ‼️ Every query MUST include WHERE tenant_id = ? — enforce via RLS
|
|
263
|
-
|
|
264
|
-
-- Row Level Security (PostgreSQL)
|
|
92
|
+
-- Pattern 1: tenant_id column (simplest — enforce via RLS)
|
|
265
93
|
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
|
|
266
|
-
|
|
267
94
|
CREATE POLICY tenant_isolation ON projects
|
|
268
95
|
USING (tenant_id = current_setting('app.current_tenant_id')::bigint);
|
|
269
96
|
|
|
270
|
-
-- Pattern 2: Schema per tenant (better isolation)
|
|
271
|
-
CREATE SCHEMA tenant_acme;
|
|
272
|
-
CREATE TABLE tenant_acme.projects (...);
|
|
97
|
+
-- Pattern 2: Schema per tenant (better isolation, harder migrations)
|
|
98
|
+
-- CREATE SCHEMA tenant_acme;
|
|
273
99
|
|
|
274
|
-
-- Pattern 3:
|
|
275
|
-
-- Only for compliance/regulatory requirements
|
|
100
|
+
-- Pattern 3: DB per tenant — only for compliance/regulatory needs
|
|
276
101
|
```
|
|
277
102
|
|
|
278
103
|
---
|
|
279
104
|
|
|
280
105
|
## ORM Selection
|
|
281
106
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
datasource db {
|
|
291
|
-
provider = "postgresql"
|
|
292
|
-
url = env("DATABASE_URL")
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
model User {
|
|
296
|
-
id Int @id @default(autoincrement())
|
|
297
|
-
email String @unique
|
|
298
|
-
name String
|
|
299
|
-
posts Post[]
|
|
300
|
-
createdAt DateTime @default(now()) @map("created_at")
|
|
301
|
-
updatedAt DateTime @updatedAt @map("updated_at")
|
|
302
|
-
|
|
303
|
-
@@map("users")
|
|
304
|
-
}
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
```typescript
|
|
308
|
-
// Usage:
|
|
309
|
-
const user = await prisma.user.findUnique({
|
|
310
|
-
where: { email: "alice@test.com" },
|
|
311
|
-
include: { posts: { take: 10, orderBy: { createdAt: "desc" } } },
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
// ❌ HALLUCINATION TRAP: Prisma uses its own query engine
|
|
315
|
-
// It does NOT support raw SQL joins in the standard query API
|
|
316
|
-
// Use prisma.$queryRaw for complex queries Prisma can't express
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### Drizzle (TypeScript — SQL-First)
|
|
107
|
+
| ORM | Best For | Trade-offs |
|
|
108
|
+
|-----|----------|------------|
|
|
109
|
+
| **Drizzle** | Edge, TypeScript, bundle-size sensitive | Newer, fewer examples |
|
|
110
|
+
| **Prisma** | DX, schema management, Prisma Studio | Heavy, NOT edge-compatible |
|
|
111
|
+
| **Kysely** | Type-safe SQL builder, full control | Manual migrations |
|
|
112
|
+
| **Raw SQL** | Complex queries, performance-critical | Manual type safety |
|
|
113
|
+
| **SQLAlchemy 2.0** | Python async ecosystem | Python only |
|
|
320
114
|
|
|
321
115
|
```typescript
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
export const users = pgTable("users", {
|
|
326
|
-
id: serial("id").primaryKey(),
|
|
327
|
-
email: text("email").notNull().unique(),
|
|
328
|
-
name: text("name").notNull(),
|
|
329
|
-
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
// Query (SQL-like, type-safe)
|
|
333
|
-
const result = await db
|
|
334
|
-
.select({ id: users.id, name: users.name })
|
|
335
|
-
.from(users)
|
|
116
|
+
// Drizzle — SQL-like, edge-compatible
|
|
117
|
+
const result = await db.select({ id: users.id, name: users.name }).from(users)
|
|
336
118
|
.where(and(eq(users.role, "admin"), eq(users.isActive, true)))
|
|
337
|
-
.orderBy(desc(users.createdAt))
|
|
338
|
-
|
|
119
|
+
.orderBy(desc(users.createdAt)).limit(20);
|
|
120
|
+
|
|
121
|
+
// Prisma — ❌ TRAP: can't express complex joins natively → use prisma.$queryRaw<Type>
|
|
122
|
+
const user = await prisma.user.findUnique({ where: { email }, include: { posts: { take: 10 } } });
|
|
339
123
|
```
|
|
340
124
|
|
|
341
125
|
---
|
|
342
126
|
|
|
343
|
-
##
|
|
344
|
-
|
|
345
|
-
```
|
|
346
|
-
Migration Rules:
|
|
347
|
-
1. Every migration must be REVERSIBLE (include down/rollback)
|
|
348
|
-
2. Never modify a migration that's been applied to production
|
|
349
|
-
3. Use explicit column types — never rely on ORM defaults
|
|
350
|
-
4. Add indexes in the SAME migration as the column
|
|
351
|
-
5. Large table migrations: add column as NULLABLE first, backfill, then add NOT NULL
|
|
352
|
-
6. Test migrations against a COPY of production data size
|
|
353
|
-
7. Never DROP a column — first remove all code references, deploy, then drop
|
|
354
|
-
```
|
|
127
|
+
## Migrations (Zero-Downtime Strategy)
|
|
355
128
|
|
|
356
129
|
```sql
|
|
357
|
-
-- Safe column
|
|
358
|
-
-- Step 1: Add nullable
|
|
130
|
+
-- Safe column add on a large production table:
|
|
131
|
+
-- Step 1: Add nullable (no lock)
|
|
359
132
|
ALTER TABLE users ADD COLUMN phone TEXT;
|
|
360
|
-
|
|
361
|
-
-- Step 2: Backfill (in batches to avoid locking)
|
|
133
|
+
-- Step 2: Backfill in batches (non-blocking)
|
|
362
134
|
UPDATE users SET phone = '' WHERE phone IS NULL AND id BETWEEN 1 AND 10000;
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
-- Step 3: Add constraint (after deploy confirms all code writes phone)
|
|
135
|
+
-- Step 3: Add constraint AFTER all code deploys write the column
|
|
366
136
|
ALTER TABLE users ALTER COLUMN phone SET NOT NULL;
|
|
367
|
-
ALTER TABLE users ALTER COLUMN phone SET DEFAULT '';
|
|
368
|
-
|
|
369
|
-
-- ❌ HALLUCINATION TRAP: Adding NOT NULL without a default locks the ENTIRE table
|
|
370
|
-
-- On large tables this can cause downtime. Always add as nullable first.
|
|
371
137
|
```
|
|
372
138
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
Application → Connection Pool → Database
|
|
379
|
-
|
|
380
|
-
Without pooling: 100 requests = 100 DB connections → DB overwhelmed
|
|
381
|
-
With pooling: 100 requests = 10-20 reused connections → DB happy
|
|
382
|
-
|
|
383
|
-
Common poolers:
|
|
384
|
-
- PgBouncer (external, most common for PostgreSQL)
|
|
385
|
-
- Prisma Accelerate (managed, for Prisma)
|
|
386
|
-
- Supabase Supavisor (managed, for Supabase)
|
|
387
|
-
- Application-level (SQLAlchemy pool, Drizzle pool)
|
|
388
|
-
|
|
389
|
-
Sizing formula:
|
|
390
|
-
max_connections = (num_cpu_cores * 2) + num_disk_spindles
|
|
391
|
-
Typical: 25-50 connections for most applications
|
|
392
|
-
|
|
393
|
-
❌ HALLUCINATION TRAP: Serverless functions need EXTERNAL pooling
|
|
394
|
-
Each Lambda/Vercel invocation opens a new connection
|
|
395
|
-
Without PgBouncer/Supavisor, you hit max_connections instantly
|
|
396
|
-
```
|
|
139
|
+
**Migration Rules:**
|
|
140
|
+
- Never modify a migration already applied to production — create a new one
|
|
141
|
+
- Remove column in 2 deploys: first remove all code references, then `DROP COLUMN`
|
|
142
|
+
- `CREATE INDEX CONCURRENTLY` to avoid table locks on existing data
|
|
143
|
+
- Test migrations against a copy of production data before running live
|
|
397
144
|
|
|
398
145
|
---
|
|
399
146
|
|
|
400
|
-
##
|
|
147
|
+
## Indexing Reference
|
|
401
148
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
─────────────────────────────────────────────────
|
|
412
|
-
VBC status: PENDING → VERIFIED
|
|
413
|
-
Evidence: [migration success / schema validation]
|
|
414
|
-
```
|
|
149
|
+
| Index Type | Use For |
|
|
150
|
+
|------------|---------|
|
|
151
|
+
| **B-tree** | General purpose — equality & range queries (default) |
|
|
152
|
+
| **Hash** | Equality-only lookups (faster than B-tree for =) |
|
|
153
|
+
| **GIN** | JSONB, arrays, full-text (`tsvector`) |
|
|
154
|
+
| **GiST** | Geometric, range types |
|
|
155
|
+
| **HNSW / IVFFlat** | Vector similarity (pgvector) |
|
|
156
|
+
|
|
157
|
+
**Composite index column order:** equality columns first → range columns last → most selective first
|
|
415
158
|
|
|
416
159
|
---
|
|
417
160
|
|
|
418
|
-
##
|
|
161
|
+
## Audit Trail
|
|
419
162
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
163
|
+
```sql
|
|
164
|
+
CREATE TABLE audit_log (
|
|
165
|
+
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
166
|
+
table_name TEXT NOT NULL, record_id BIGINT NOT NULL,
|
|
167
|
+
action TEXT NOT NULL CHECK (action IN ('INSERT', 'UPDATE', 'DELETE')),
|
|
168
|
+
old_data JSONB, new_data JSONB,
|
|
169
|
+
changed_by BIGINT REFERENCES users(id),
|
|
170
|
+
changed_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
171
|
+
);
|
|
172
|
+
CREATE INDEX idx_audit_log_table_record ON audit_log (table_name, record_id);
|
|
173
|
+
CREATE INDEX idx_audit_log_changed_at ON audit_log USING brin (changed_at); -- BRIN for time-ordered append-only tables
|
|
174
|
+
```
|
|
430
175
|
|
|
431
176
|
---
|
|
432
177
|
|
|
433
|
-
##
|
|
434
|
-
|
|
435
|
-
**Slash command: `/tribunal-database`**
|
|
436
|
-
|
|
437
|
-
### ✅ Pre-Flight Self-Audit
|
|
178
|
+
## Connection Pooling
|
|
438
179
|
|
|
439
180
|
```
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
✅ Did I use BIGINT or UUID v7 for primary keys?
|
|
443
|
-
✅ Are all table/column names snake_case?
|
|
444
|
-
✅ Do all tables have created_at and updated_at?
|
|
445
|
-
✅ Is the migration reversible?
|
|
446
|
-
✅ Did I use parameterized queries (not string interpolation)?
|
|
447
|
-
✅ Is connection pooling configured for serverless?
|
|
448
|
-
✅ Is multi-tenant data isolated (RLS or schema)?
|
|
449
|
-
✅ Did I run EXPLAIN ANALYZE on critical queries?
|
|
450
|
-
```
|
|
181
|
+
Without pooling: 100 concurrent requests → 100 DB connections → overwhelms DB
|
|
182
|
+
With pooling: 100 concurrent requests → 10–20 reused connections
|
|
451
183
|
|
|
452
|
-
|
|
184
|
+
Sizing formula: max_connections = (cpu_cores × 2) + disk_spindles (typically 25–50)
|
|
453
185
|
|
|
454
|
-
|
|
455
|
-
|
|
186
|
+
Poolers:
|
|
187
|
+
PgBouncer → External, most common for self-hosted Postgres
|
|
188
|
+
Prisma Accelerate → Managed, for Prisma projects
|
|
189
|
+
Supabase Supavisor → Managed, for Supabase projects
|
|
190
|
+
```
|