jorgex-stack 1.0.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/LICENSE +21 -0
- package/PRD.md +297 -0
- package/README.md +58 -0
- package/dist/cli.js +2894 -0
- package/package.json +51 -0
- package/stack/agents/README.md +33 -0
- package/stack/agents/backend-analyst.md +61 -0
- package/stack/agents/code-reviewer.md +66 -0
- package/stack/agents/code-simplifier.md +81 -0
- package/stack/agents/comment-fixer.md +54 -0
- package/stack/agents/docs-maintainer.md +88 -0
- package/stack/agents/engram.md +124 -0
- package/stack/agents/frontend-analyst.md +51 -0
- package/stack/agents/implementer.md +55 -0
- package/stack/agents/orchestrator.md +192 -0
- package/stack/agents/security-auditor.md +66 -0
- package/stack/agents/silent-failure-hunter.md +167 -0
- package/stack/agents/test-analyzer.md +91 -0
- package/stack/agents/tester.md +71 -0
- package/stack/agents/translator.md +101 -0
- package/stack/agents/type-design-analyzer.md +128 -0
- package/stack/commands/xreview.md +80 -0
- package/stack/config/defaults.json +37 -0
- package/stack/hooks/hooks.json +18 -0
- package/stack/mcp/servers.json +19 -0
- package/stack/plugins/opencode/engram.ts +378 -0
- package/stack/plugins/opencode/hooks.ts +766 -0
- package/stack/plugins/opencode/package.json +10 -0
- package/stack/plugins/opencode/worktree.ts +405 -0
- package/stack/scripts/post-pr-review.cjs +156 -0
- package/stack/skills/agent-browser/SKILL.md +55 -0
- package/stack/skills/agent-delegation/SKILL.md +58 -0
- package/stack/skills/deploy-to-vercel/SKILL.md +296 -0
- package/stack/skills/deploy-to-vercel/resources/deploy-codex.sh +301 -0
- package/stack/skills/deploy-to-vercel/resources/deploy.sh +301 -0
- package/stack/skills/diagnose/SKILL.md +117 -0
- package/stack/skills/diagnose/scripts/hitl-loop.template.sh +41 -0
- package/stack/skills/find-skills/SKILL.md +133 -0
- package/stack/skills/graphify/.graphify_version +1 -0
- package/stack/skills/graphify/SKILL.md +1319 -0
- package/stack/skills/mcp-builder/LICENSE.txt +202 -0
- package/stack/skills/mcp-builder/SKILL.md +236 -0
- package/stack/skills/mcp-builder/reference/evaluation.md +602 -0
- package/stack/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
- package/stack/skills/mcp-builder/reference/node_mcp_server.md +970 -0
- package/stack/skills/mcp-builder/reference/python_mcp_server.md +719 -0
- package/stack/skills/mcp-builder/scripts/connections.py +151 -0
- package/stack/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/stack/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/stack/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/stack/skills/obsidian-cli/SKILL.md +106 -0
- package/stack/skills/obsidian-markdown/SKILL.md +196 -0
- package/stack/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
- package/stack/skills/obsidian-markdown/references/EMBEDS.md +63 -0
- package/stack/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
- package/stack/skills/react-doctor/SKILL.md +19 -0
- package/stack/skills/skill-creator/LICENSE.txt +202 -0
- package/stack/skills/skill-creator/SKILL.md +485 -0
- package/stack/skills/skill-creator/agents/analyzer.md +274 -0
- package/stack/skills/skill-creator/agents/comparator.md +202 -0
- package/stack/skills/skill-creator/agents/grader.md +223 -0
- package/stack/skills/skill-creator/assets/eval_review.html +146 -0
- package/stack/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/stack/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/stack/skills/skill-creator/references/schemas.md +430 -0
- package/stack/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/stack/skills/skill-creator/scripts/generate_report.py +326 -0
- package/stack/skills/skill-creator/scripts/improve_description.py +248 -0
- package/stack/skills/skill-creator/scripts/package_skill.py +136 -0
- package/stack/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/stack/skills/skill-creator/scripts/run_eval.py +310 -0
- package/stack/skills/skill-creator/scripts/run_loop.py +332 -0
- package/stack/skills/skill-creator/scripts/utils.py +47 -0
- package/stack/skills/supabase/SKILL.md +135 -0
- package/stack/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/stack/skills/supabase/references/skill-feedback.md +17 -0
- package/stack/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/stack/skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
- package/stack/skills/supabase-postgres-best-practices/references/_sections.md +39 -0
- package/stack/skills/supabase-postgres-best-practices/references/_template.md +34 -0
- package/stack/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/stack/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/stack/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/stack/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/stack/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/stack/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/stack/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/stack/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/stack/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/stack/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/stack/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/stack/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/stack/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/stack/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/stack/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/stack/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/stack/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/stack/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/stack/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/stack/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/stack/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/stack/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/stack/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/stack/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/stack/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/stack/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/stack/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/stack/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/stack/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/stack/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/stack/skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
- package/stack/skills/tdd/SKILL.md +109 -0
- package/stack/skills/tdd/deep-modules.md +33 -0
- package/stack/skills/tdd/interface-design.md +31 -0
- package/stack/skills/tdd/mocking.md +59 -0
- package/stack/skills/tdd/refactoring.md +10 -0
- package/stack/skills/tdd/tests.md +61 -0
- package/stack/skills/to-issues/SKILL.md +83 -0
- package/stack/skills/to-prd/SKILL.md +72 -0
- package/stack/skills/work-lifecycle/SKILL.md +79 -0
- package/stack/skills/work-lifecycle/references/plan-template.md +185 -0
- package/stack/system-prompt/AGENTS.md +171 -0
- package/stack/system-prompt/engram-protocol.md +53 -0
- package/upstreams.json +96 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Add Constraints Safely in Migrations
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Prevents migration failures and enables idempotent schema changes
|
|
5
|
+
tags: constraints, migrations, schema, alter-table
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Add Constraints Safely in Migrations
|
|
9
|
+
|
|
10
|
+
PostgreSQL does not support `ADD CONSTRAINT IF NOT EXISTS`. Migrations using this syntax will fail.
|
|
11
|
+
|
|
12
|
+
**Incorrect (causes syntax error):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- ERROR: syntax error at or near "not" (SQLSTATE 42601)
|
|
16
|
+
alter table public.profiles
|
|
17
|
+
add constraint if not exists profiles_birthchart_id_unique unique (birthchart_id);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (idempotent constraint creation):**
|
|
21
|
+
|
|
22
|
+
```sql
|
|
23
|
+
-- Use DO block to check before adding
|
|
24
|
+
do $$
|
|
25
|
+
begin
|
|
26
|
+
if not exists (
|
|
27
|
+
select 1 from pg_constraint
|
|
28
|
+
where conname = 'profiles_birthchart_id_unique'
|
|
29
|
+
and conrelid = 'public.profiles'::regclass
|
|
30
|
+
) then
|
|
31
|
+
alter table public.profiles
|
|
32
|
+
add constraint profiles_birthchart_id_unique unique (birthchart_id);
|
|
33
|
+
end if;
|
|
34
|
+
end $$;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For all constraint types:
|
|
38
|
+
|
|
39
|
+
```sql
|
|
40
|
+
-- Check constraints
|
|
41
|
+
do $$
|
|
42
|
+
begin
|
|
43
|
+
if not exists (
|
|
44
|
+
select 1 from pg_constraint
|
|
45
|
+
where conname = 'check_age_positive'
|
|
46
|
+
) then
|
|
47
|
+
alter table users add constraint check_age_positive check (age > 0);
|
|
48
|
+
end if;
|
|
49
|
+
end $$;
|
|
50
|
+
|
|
51
|
+
-- Foreign keys
|
|
52
|
+
do $$
|
|
53
|
+
begin
|
|
54
|
+
if not exists (
|
|
55
|
+
select 1 from pg_constraint
|
|
56
|
+
where conname = 'profiles_birthchart_id_fkey'
|
|
57
|
+
) then
|
|
58
|
+
alter table profiles
|
|
59
|
+
add constraint profiles_birthchart_id_fkey
|
|
60
|
+
foreign key (birthchart_id) references birthcharts(id);
|
|
61
|
+
end if;
|
|
62
|
+
end $$;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Check if constraint exists:
|
|
66
|
+
|
|
67
|
+
```sql
|
|
68
|
+
-- Query to check constraint existence
|
|
69
|
+
select conname, contype, pg_get_constraintdef(oid)
|
|
70
|
+
from pg_constraint
|
|
71
|
+
where conrelid = 'public.profiles'::regclass;
|
|
72
|
+
|
|
73
|
+
-- contype values:
|
|
74
|
+
-- 'p' = PRIMARY KEY
|
|
75
|
+
-- 'f' = FOREIGN KEY
|
|
76
|
+
-- 'u' = UNIQUE
|
|
77
|
+
-- 'c' = CHECK
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Reference: [Constraints](https://www.postgresql.org/docs/current/ddl-constraints.html)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Choose Appropriate Data Types
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: 50% storage reduction, faster comparisons
|
|
5
|
+
tags: data-types, schema, storage, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Choose Appropriate Data Types
|
|
9
|
+
|
|
10
|
+
Using the right data types reduces storage, improves query performance, and prevents bugs.
|
|
11
|
+
|
|
12
|
+
**Incorrect (wrong data types):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
create table users (
|
|
16
|
+
id int, -- Will overflow at 2.1 billion
|
|
17
|
+
email varchar(255), -- Unnecessary length limit
|
|
18
|
+
created_at timestamp, -- Missing timezone info
|
|
19
|
+
is_active varchar(5), -- String for boolean
|
|
20
|
+
price varchar(20) -- String for numeric
|
|
21
|
+
);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Correct (appropriate data types):**
|
|
25
|
+
|
|
26
|
+
```sql
|
|
27
|
+
create table users (
|
|
28
|
+
id bigint generated always as identity primary key, -- 9 quintillion max
|
|
29
|
+
email text, -- No artificial limit, same performance as varchar
|
|
30
|
+
created_at timestamptz, -- Always store timezone-aware timestamps
|
|
31
|
+
is_active boolean default true, -- 1 byte vs variable string length
|
|
32
|
+
price numeric(10,2) -- Exact decimal arithmetic
|
|
33
|
+
);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Key guidelines:
|
|
37
|
+
|
|
38
|
+
```sql
|
|
39
|
+
-- IDs: use bigint, not int (future-proofing)
|
|
40
|
+
-- Strings: use text, not varchar(n) unless constraint needed
|
|
41
|
+
-- Time: use timestamptz, not timestamp
|
|
42
|
+
-- Money: use numeric, not float (precision matters)
|
|
43
|
+
-- Enums: use text with check constraint or create enum type
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Reference: [Data Types](https://www.postgresql.org/docs/current/datatype.html)
|
package/stack/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Index Foreign Key Columns
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: 10-100x faster JOINs and CASCADE operations
|
|
5
|
+
tags: foreign-key, indexes, joins, schema
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Index Foreign Key Columns
|
|
9
|
+
|
|
10
|
+
Postgres does not automatically index foreign key columns. Missing indexes cause slow JOINs and CASCADE operations.
|
|
11
|
+
|
|
12
|
+
**Incorrect (unindexed foreign key):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
create table orders (
|
|
16
|
+
id bigint generated always as identity primary key,
|
|
17
|
+
customer_id bigint references customers(id) on delete cascade,
|
|
18
|
+
total numeric(10,2)
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
-- No index on customer_id!
|
|
22
|
+
-- JOINs and ON DELETE CASCADE both require full table scan
|
|
23
|
+
select * from orders where customer_id = 123; -- Seq Scan
|
|
24
|
+
delete from customers where id = 123; -- Locks table, scans all orders
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (indexed foreign key):**
|
|
28
|
+
|
|
29
|
+
```sql
|
|
30
|
+
create table orders (
|
|
31
|
+
id bigint generated always as identity primary key,
|
|
32
|
+
customer_id bigint references customers(id) on delete cascade,
|
|
33
|
+
total numeric(10,2)
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
-- Always index the FK column
|
|
37
|
+
create index orders_customer_id_idx on orders (customer_id);
|
|
38
|
+
|
|
39
|
+
-- Now JOINs and cascades are fast
|
|
40
|
+
select * from orders where customer_id = 123; -- Index Scan
|
|
41
|
+
delete from customers where id = 123; -- Uses index, fast cascade
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Find missing FK indexes:
|
|
45
|
+
|
|
46
|
+
```sql
|
|
47
|
+
select
|
|
48
|
+
conrelid::regclass as table_name,
|
|
49
|
+
a.attname as fk_column
|
|
50
|
+
from pg_constraint c
|
|
51
|
+
join pg_attribute a on a.attrelid = c.conrelid and a.attnum = any(c.conkey)
|
|
52
|
+
where c.contype = 'f'
|
|
53
|
+
and not exists (
|
|
54
|
+
select 1 from pg_index i
|
|
55
|
+
where i.indrelid = c.conrelid and a.attnum = any(i.indkey)
|
|
56
|
+
);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Reference: [Foreign Keys](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-FK)
|
package/stack/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Lowercase Identifiers for Compatibility
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Avoid case-sensitivity bugs with tools, ORMs, and AI assistants
|
|
5
|
+
tags: naming, identifiers, case-sensitivity, schema, conventions
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Lowercase Identifiers for Compatibility
|
|
9
|
+
|
|
10
|
+
PostgreSQL folds unquoted identifiers to lowercase. Quoted mixed-case identifiers require quotes forever and cause issues with tools, ORMs, and AI assistants that may not recognize them.
|
|
11
|
+
|
|
12
|
+
**Incorrect (mixed-case identifiers):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Quoted identifiers preserve case but require quotes everywhere
|
|
16
|
+
CREATE TABLE "Users" (
|
|
17
|
+
"userId" bigint PRIMARY KEY,
|
|
18
|
+
"firstName" text,
|
|
19
|
+
"lastName" text
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
-- Must always quote or queries fail
|
|
23
|
+
SELECT "firstName" FROM "Users" WHERE "userId" = 1;
|
|
24
|
+
|
|
25
|
+
-- This fails - Users becomes users without quotes
|
|
26
|
+
SELECT firstName FROM Users;
|
|
27
|
+
-- ERROR: relation "users" does not exist
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Correct (lowercase snake_case):**
|
|
31
|
+
|
|
32
|
+
```sql
|
|
33
|
+
-- Unquoted lowercase identifiers are portable and tool-friendly
|
|
34
|
+
CREATE TABLE users (
|
|
35
|
+
user_id bigint PRIMARY KEY,
|
|
36
|
+
first_name text,
|
|
37
|
+
last_name text
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
-- Works without quotes, recognized by all tools
|
|
41
|
+
SELECT first_name FROM users WHERE user_id = 1;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Common sources of mixed-case identifiers:
|
|
45
|
+
|
|
46
|
+
```sql
|
|
47
|
+
-- ORMs often generate quoted camelCase - configure them to use snake_case
|
|
48
|
+
-- Migrations from other databases may preserve original casing
|
|
49
|
+
-- Some GUI tools quote identifiers by default - disable this
|
|
50
|
+
|
|
51
|
+
-- If stuck with mixed-case, create views as a compatibility layer
|
|
52
|
+
CREATE VIEW users AS SELECT "userId" AS user_id, "firstName" AS first_name FROM "Users";
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Reference: [Identifiers and Key Words](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Partition Large Tables for Better Performance
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: 5-20x faster queries and maintenance on large tables
|
|
5
|
+
tags: partitioning, large-tables, time-series, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Partition Large Tables for Better Performance
|
|
9
|
+
|
|
10
|
+
Partitioning splits a large table into smaller pieces, improving query performance and maintenance operations.
|
|
11
|
+
|
|
12
|
+
**Incorrect (single large table):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
create table events (
|
|
16
|
+
id bigint generated always as identity,
|
|
17
|
+
created_at timestamptz,
|
|
18
|
+
data jsonb
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
-- 500M rows, queries scan everything
|
|
22
|
+
select * from events where created_at > '2024-01-01'; -- Slow
|
|
23
|
+
vacuum events; -- Takes hours, locks table
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct (partitioned by time range):**
|
|
27
|
+
|
|
28
|
+
```sql
|
|
29
|
+
create table events (
|
|
30
|
+
id bigint generated always as identity,
|
|
31
|
+
created_at timestamptz not null,
|
|
32
|
+
data jsonb
|
|
33
|
+
) partition by range (created_at);
|
|
34
|
+
|
|
35
|
+
-- Create partitions for each month
|
|
36
|
+
create table events_2024_01 partition of events
|
|
37
|
+
for values from ('2024-01-01') to ('2024-02-01');
|
|
38
|
+
|
|
39
|
+
create table events_2024_02 partition of events
|
|
40
|
+
for values from ('2024-02-01') to ('2024-03-01');
|
|
41
|
+
|
|
42
|
+
-- Queries only scan relevant partitions
|
|
43
|
+
select * from events where created_at > '2024-01-15'; -- Only scans events_2024_01+
|
|
44
|
+
|
|
45
|
+
-- Drop old data instantly
|
|
46
|
+
drop table events_2023_01; -- Instant vs DELETE taking hours
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
When to partition:
|
|
50
|
+
|
|
51
|
+
- Tables > 100M rows
|
|
52
|
+
- Time-series data with date-based queries
|
|
53
|
+
- Need to efficiently drop old data
|
|
54
|
+
|
|
55
|
+
Reference: [Table Partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Select Optimal Primary Key Strategy
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Better index locality, reduced fragmentation
|
|
5
|
+
tags: primary-key, identity, uuid, serial, schema
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Select Optimal Primary Key Strategy
|
|
9
|
+
|
|
10
|
+
Primary key choice affects insert performance, index size, and replication
|
|
11
|
+
efficiency.
|
|
12
|
+
|
|
13
|
+
**Incorrect (problematic PK choices):**
|
|
14
|
+
|
|
15
|
+
```sql
|
|
16
|
+
-- identity is the SQL-standard approach
|
|
17
|
+
create table users (
|
|
18
|
+
id serial primary key -- Works, but IDENTITY is recommended
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
-- Random UUIDs (v4) cause index fragmentation
|
|
22
|
+
create table orders (
|
|
23
|
+
id uuid default gen_random_uuid() primary key -- UUIDv4 = random = scattered inserts
|
|
24
|
+
);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (optimal PK strategies):**
|
|
28
|
+
|
|
29
|
+
```sql
|
|
30
|
+
-- Use IDENTITY for sequential IDs (SQL-standard, best for most cases)
|
|
31
|
+
create table users (
|
|
32
|
+
id bigint generated always as identity primary key
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- For distributed systems needing UUIDs, use UUIDv7 (time-ordered)
|
|
36
|
+
-- Requires pg_uuidv7 extension: create extension pg_uuidv7;
|
|
37
|
+
create table orders (
|
|
38
|
+
id uuid default uuid_generate_v7() primary key -- Time-ordered, no fragmentation
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
-- Alternative: time-prefixed IDs for sortable, distributed IDs (no extension needed)
|
|
42
|
+
create table events (
|
|
43
|
+
id text default concat(
|
|
44
|
+
to_char(now() at time zone 'utc', 'YYYYMMDDHH24MISSMS'),
|
|
45
|
+
gen_random_uuid()::text
|
|
46
|
+
) primary key
|
|
47
|
+
);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Guidelines:
|
|
51
|
+
|
|
52
|
+
- Single database: `bigint identity` (sequential, 8 bytes, SQL-standard)
|
|
53
|
+
- Distributed/exposed IDs: UUIDv7 (requires pg_uuidv7) or ULID (time-ordered, no
|
|
54
|
+
fragmentation)
|
|
55
|
+
- `serial` works but `identity` is SQL-standard and preferred for new
|
|
56
|
+
applications
|
|
57
|
+
- Avoid random UUIDs (v4) as primary keys on large tables (causes index
|
|
58
|
+
fragmentation)
|
|
59
|
+
|
|
60
|
+
Reference:
|
|
61
|
+
[Identity Columns](https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-GENERATED-IDENTITY)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Apply Principle of Least Privilege
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Reduced attack surface, better audit trail
|
|
5
|
+
tags: privileges, security, roles, permissions
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Apply Principle of Least Privilege
|
|
9
|
+
|
|
10
|
+
Grant only the minimum permissions required. Never use superuser for application queries.
|
|
11
|
+
|
|
12
|
+
**Incorrect (overly broad permissions):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Application uses superuser connection
|
|
16
|
+
-- Or grants ALL to application role
|
|
17
|
+
grant all privileges on all tables in schema public to app_user;
|
|
18
|
+
grant all privileges on all sequences in schema public to app_user;
|
|
19
|
+
|
|
20
|
+
-- Any SQL injection becomes catastrophic
|
|
21
|
+
-- drop table users; cascades to everything
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Correct (minimal, specific grants):**
|
|
25
|
+
|
|
26
|
+
```sql
|
|
27
|
+
-- Create role with no default privileges
|
|
28
|
+
create role app_readonly nologin;
|
|
29
|
+
|
|
30
|
+
-- Grant only SELECT on specific tables
|
|
31
|
+
grant usage on schema public to app_readonly;
|
|
32
|
+
grant select on public.products, public.categories to app_readonly;
|
|
33
|
+
|
|
34
|
+
-- Create role for writes with limited scope
|
|
35
|
+
create role app_writer nologin;
|
|
36
|
+
grant usage on schema public to app_writer;
|
|
37
|
+
grant select, insert, update on public.orders to app_writer;
|
|
38
|
+
grant usage on sequence orders_id_seq to app_writer;
|
|
39
|
+
-- No DELETE permission
|
|
40
|
+
|
|
41
|
+
-- Login role inherits from these
|
|
42
|
+
create role app_user login password 'xxx';
|
|
43
|
+
grant app_writer to app_user;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Revoke public defaults:
|
|
47
|
+
|
|
48
|
+
```sql
|
|
49
|
+
-- Revoke default public access
|
|
50
|
+
revoke all on schema public from public;
|
|
51
|
+
revoke all on all tables in schema public from public;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Reference: [Roles and Privileges](https://supabase.com/blog/postgres-roles-and-privileges)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Enable Row Level Security for Multi-Tenant Data
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: Database-enforced tenant isolation, prevent data leaks
|
|
5
|
+
tags: rls, row-level-security, multi-tenant, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Enable Row Level Security for Multi-Tenant Data
|
|
9
|
+
|
|
10
|
+
Row Level Security (RLS) enforces data access at the database level, ensuring users only see their own data.
|
|
11
|
+
|
|
12
|
+
**Incorrect (application-level filtering only):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Relying only on application to filter
|
|
16
|
+
select * from orders where user_id = $current_user_id;
|
|
17
|
+
|
|
18
|
+
-- Bug or bypass means all data is exposed!
|
|
19
|
+
select * from orders; -- Returns ALL orders
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (database-enforced RLS):**
|
|
23
|
+
|
|
24
|
+
```sql
|
|
25
|
+
-- Enable RLS on the table
|
|
26
|
+
alter table orders enable row level security;
|
|
27
|
+
|
|
28
|
+
-- Create policy for users to see only their orders
|
|
29
|
+
create policy orders_user_policy on orders
|
|
30
|
+
for all
|
|
31
|
+
using (user_id = current_setting('app.current_user_id')::bigint);
|
|
32
|
+
|
|
33
|
+
-- Force RLS even for table owners
|
|
34
|
+
alter table orders force row level security;
|
|
35
|
+
|
|
36
|
+
-- Set user context and query
|
|
37
|
+
set app.current_user_id = '123';
|
|
38
|
+
select * from orders; -- Only returns orders for user 123
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Policy for authenticated role:
|
|
42
|
+
|
|
43
|
+
```sql
|
|
44
|
+
create policy orders_user_policy on orders
|
|
45
|
+
for all
|
|
46
|
+
to authenticated
|
|
47
|
+
using (user_id = auth.uid());
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Reference: [Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Optimize RLS Policies for Performance
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: 5-10x faster RLS queries with proper patterns
|
|
5
|
+
tags: rls, performance, security, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Optimize RLS Policies for Performance
|
|
9
|
+
|
|
10
|
+
Poorly written RLS policies can cause severe performance issues. Use subqueries and indexes strategically.
|
|
11
|
+
|
|
12
|
+
**Incorrect (function called for every row):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
create policy orders_policy on orders
|
|
16
|
+
using (auth.uid() = user_id); -- auth.uid() called per row!
|
|
17
|
+
|
|
18
|
+
-- With 1M rows, auth.uid() is called 1M times
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (wrap functions in SELECT):**
|
|
22
|
+
|
|
23
|
+
```sql
|
|
24
|
+
create policy orders_policy on orders
|
|
25
|
+
using ((select auth.uid()) = user_id); -- Called once, cached
|
|
26
|
+
|
|
27
|
+
-- 100x+ faster on large tables
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use security definer functions for complex checks:
|
|
31
|
+
|
|
32
|
+
`SECURITY DEFINER` functions run with the creator's privileges and bypass RLS on any tables they touch — which is what makes them useful for internal lookups, but also what makes them dangerous if misused. Always include an explicit `auth.uid()` check inside the function body, keep them in a non-exposed schema, and revoke `EXECUTE` from any role that shouldn't call them directly.
|
|
33
|
+
|
|
34
|
+
```sql
|
|
35
|
+
-- Create helper function in a private schema
|
|
36
|
+
create or replace function private.is_team_member(team_id bigint)
|
|
37
|
+
returns boolean
|
|
38
|
+
language sql
|
|
39
|
+
security definer
|
|
40
|
+
set search_path = ''
|
|
41
|
+
as $$
|
|
42
|
+
select exists (
|
|
43
|
+
select 1 from public.team_members
|
|
44
|
+
-- always check the calling user's identity inside the function
|
|
45
|
+
where team_id = $1 and user_id = (select auth.uid())
|
|
46
|
+
);
|
|
47
|
+
$$;
|
|
48
|
+
|
|
49
|
+
-- Revoke direct execution from public roles
|
|
50
|
+
revoke execute on function private.is_team_member(bigint) from PUBLIC, anon, authenticated, service_role;
|
|
51
|
+
|
|
52
|
+
-- Use in policy (indexed lookup, not per-row check)
|
|
53
|
+
create policy team_orders_policy on orders
|
|
54
|
+
using ((select private.is_team_member(team_id)));
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Always add indexes on columns used in RLS policies:
|
|
58
|
+
|
|
59
|
+
```sql
|
|
60
|
+
create index orders_user_id_idx on orders (user_id);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Reference: [RLS Performance](https://supabase.com/docs/guides/database/postgres/row-level-security#rls-performance-recommendations)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd
|
|
3
|
+
description: Test-driven development with red-green-refactor loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test-Driven Development
|
|
7
|
+
|
|
8
|
+
## Philosophy
|
|
9
|
+
|
|
10
|
+
**Core principle**: Tests should verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't.
|
|
11
|
+
|
|
12
|
+
**Good tests** are integration-style: they exercise real code paths through public APIs. They describe _what_ the system does, not _how_ it does it. A good test reads like a specification - "user can checkout with valid cart" tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure.
|
|
13
|
+
|
|
14
|
+
**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.
|
|
15
|
+
|
|
16
|
+
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
|
|
17
|
+
|
|
18
|
+
## Anti-Pattern: Horizontal Slices
|
|
19
|
+
|
|
20
|
+
**DO NOT write all tests first, then all implementation.** This is "horizontal slicing" - treating RED as "write all tests" and GREEN as "write all code."
|
|
21
|
+
|
|
22
|
+
This produces **crap tests**:
|
|
23
|
+
|
|
24
|
+
- Tests written in bulk test _imagined_ behavior, not _actual_ behavior
|
|
25
|
+
- You end up testing the _shape_ of things (data structures, function signatures) rather than user-facing behavior
|
|
26
|
+
- Tests become insensitive to real changes - they pass when behavior breaks, fail when behavior is fine
|
|
27
|
+
- You outrun your headlights, committing to test structure before understanding the implementation
|
|
28
|
+
|
|
29
|
+
**Correct approach**: Vertical slices via tracer bullets. One test → one implementation → repeat. Each test responds to what you learned from the previous cycle. Because you just wrote the code, you know exactly what behavior matters and how to verify it.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
WRONG (horizontal):
|
|
33
|
+
RED: test1, test2, test3, test4, test5
|
|
34
|
+
GREEN: impl1, impl2, impl3, impl4, impl5
|
|
35
|
+
|
|
36
|
+
RIGHT (vertical):
|
|
37
|
+
RED→GREEN: test1→impl1
|
|
38
|
+
RED→GREEN: test2→impl2
|
|
39
|
+
RED→GREEN: test3→impl3
|
|
40
|
+
...
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Workflow
|
|
44
|
+
|
|
45
|
+
### 1. Planning
|
|
46
|
+
|
|
47
|
+
When exploring the codebase, use the project's domain glossary so that test names and interface vocabulary match the project's language, and respect ADRs in the area you're touching.
|
|
48
|
+
|
|
49
|
+
Before writing any code:
|
|
50
|
+
|
|
51
|
+
- [ ] Confirm with user what interface changes are needed
|
|
52
|
+
- [ ] Confirm with user which behaviors to test (prioritize)
|
|
53
|
+
- [ ] Identify opportunities for [deep modules](deep-modules.md) (small interface, deep implementation)
|
|
54
|
+
- [ ] Design interfaces for [testability](interface-design.md)
|
|
55
|
+
- [ ] List the behaviors to test (not implementation steps)
|
|
56
|
+
- [ ] Get user approval on the plan
|
|
57
|
+
|
|
58
|
+
Ask: "What should the public interface look like? Which behaviors are most important to test?"
|
|
59
|
+
|
|
60
|
+
**You can't test everything.** Confirm with the user exactly which behaviors matter most. Focus testing effort on critical paths and complex logic, not every possible edge case.
|
|
61
|
+
|
|
62
|
+
### 2. Tracer Bullet
|
|
63
|
+
|
|
64
|
+
Write ONE test that confirms ONE thing about the system:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
RED: Write test for first behavior → test fails
|
|
68
|
+
GREEN: Write minimal code to pass → test passes
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This is your tracer bullet - proves the path works end-to-end.
|
|
72
|
+
|
|
73
|
+
### 3. Incremental Loop
|
|
74
|
+
|
|
75
|
+
For each remaining behavior:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
RED: Write next test → fails
|
|
79
|
+
GREEN: Minimal code to pass → passes
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Rules:
|
|
83
|
+
|
|
84
|
+
- One test at a time
|
|
85
|
+
- Only enough code to pass current test
|
|
86
|
+
- Don't anticipate future tests
|
|
87
|
+
- Keep tests focused on observable behavior
|
|
88
|
+
|
|
89
|
+
### 4. Refactor
|
|
90
|
+
|
|
91
|
+
After all tests pass, look for [refactor candidates](refactoring.md):
|
|
92
|
+
|
|
93
|
+
- [ ] Extract duplication
|
|
94
|
+
- [ ] Deepen modules (move complexity behind simple interfaces)
|
|
95
|
+
- [ ] Apply SOLID principles where natural
|
|
96
|
+
- [ ] Consider what new code reveals about existing code
|
|
97
|
+
- [ ] Run tests after each refactor step
|
|
98
|
+
|
|
99
|
+
**Never refactor while RED.** Get to GREEN first.
|
|
100
|
+
|
|
101
|
+
## Checklist Per Cycle
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
[ ] Test describes behavior, not implementation
|
|
105
|
+
[ ] Test uses public interface only
|
|
106
|
+
[ ] Test would survive internal refactor
|
|
107
|
+
[ ] Code is minimal for this test
|
|
108
|
+
[ ] No speculative features added
|
|
109
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Deep Modules
|
|
2
|
+
|
|
3
|
+
From "A Philosophy of Software Design":
|
|
4
|
+
|
|
5
|
+
**Deep module** = small interface + lots of implementation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────┐
|
|
9
|
+
│ Small Interface │ ← Few methods, simple params
|
|
10
|
+
├─────────────────────┤
|
|
11
|
+
│ │
|
|
12
|
+
│ │
|
|
13
|
+
│ Deep Implementation│ ← Complex logic hidden
|
|
14
|
+
│ │
|
|
15
|
+
│ │
|
|
16
|
+
└─────────────────────┘
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Shallow module** = large interface + little implementation (avoid)
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────┐
|
|
23
|
+
│ Large Interface │ ← Many methods, complex params
|
|
24
|
+
├─────────────────────────────────┤
|
|
25
|
+
│ Thin Implementation │ ← Just passes through
|
|
26
|
+
└─────────────────────────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
When designing interfaces, ask:
|
|
30
|
+
|
|
31
|
+
- Can I reduce the number of methods?
|
|
32
|
+
- Can I simplify the parameters?
|
|
33
|
+
- Can I hide more complexity inside?
|