From cortex
This skill should be used when writing, reviewing, or fixing a Supabase migration, creating a new database table, adding columns or indexes, writing seed data with foreign keys, or debugging a migration that failed or rolled back. Covers IMMUTABLE constraints, transactional rollback, RLS+policies+grants, IF NOT EXISTS patterns, constraint naming, and data type discipline.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cortex:migration-safetyThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**TL;DR**: 8 rules that prevent migration failures. Every one is based on a real incident.
TL;DR: 8 rules that prevent migration failures. Every one is based on a real incident.
now(), CURRENT_DATE, and clock_timestamp() are NOT IMMUTABLE. They are STABLE (return different values across transactions). PostgreSQL requires all functions in partial index WHERE clauses to be IMMUTABLE.
Never: CREATE INDEX ... WHERE expires_at > now()
Instead: Include the time column as a trailing index column:
CREATE INDEX idx_foo ON tbl(col1, col2, expires_at);
The query planner still uses the index efficiently when queries filter WHERE expires_at > now() at runtime.
Supabase wraps each migration file in a transaction. If ANY statement fails (an INSERT with a FK violation, a constraint error), the ENTIRE migration rolls back — including CREATE TABLE, ALTER TABLE, CREATE POLICY, everything.
Implication: Fix migrations must include ALL statements from the original (table creation, RLS, policies, seed data), not just the "missing" part. You cannot rely on partial success.
PostgreSQL auto-names constraints differently from explicit names. A migration may specify uq_ai_briefs_ticker but production may have ai_briefs_ticker_key (Postgres default pattern: {table}_{column}_key for UNIQUE, {table}_{column}_fkey for FK).
Always: Use dual-name IF EXISTS:
ALTER TABLE ai_briefs DROP CONSTRAINT IF EXISTS uq_ai_briefs_ticker;
ALTER TABLE ai_briefs DROP CONSTRAINT IF EXISTS ai_briefs_ticker_key;
Before writing: Check actual production constraint names via Supabase dashboard or \d+ tablename.
Always use defensive patterns to prevent "already exists" failures on re-run or fix migrations:
CREATE TABLE IF NOT EXISTSCREATE INDEX IF NOT EXISTSALTER TABLE ... ADD COLUMN IF NOT EXISTS (PostgreSQL 11+)DO $$ BEGIN ... EXCEPTION WHEN duplicate_object THEN NULL; END $$; for policies and constraints:DO $$ BEGIN
ALTER TABLE tbl ADD CONSTRAINT uq_foo UNIQUE (col);
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
Every new table needs all three:
ALTER TABLE new_table ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow authenticated read" ON new_table
FOR SELECT TO authenticated USING (true);
GRANT SELECT ON new_table TO authenticated;
Missing any one of these causes silent failures — RLS without policies = no rows returned, policies without GRANT = permission denied.
INSERT statements with foreign key constraints must guard against missing references:
INSERT INTO ticker_aliases (alias, canonical_ticker)
SELECT 'FB', 'META'
WHERE EXISTS (SELECT 1 FROM stocks WHERE ticker = 'META');
Without the guard, a FK violation rolls back the entire migration (Rule 2).
Match the ingestion boundary:
Math.round() / int() for: dollar amounts, volume, share counts (whole), market capsupabase db reset locally before pushing to verify the full migration chainSee references/migration-incidents.md for 5 real incidents from this codebase.
npx claudepluginhub crombieman/undercurrent-cortex --plugin cortexGuides collaborative design exploration before implementation: explores context, asks clarifying questions, proposes approaches, and writes a design doc for user approval.
Creates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.
Resolves in-progress git merge or rebase conflicts by analyzing history, understanding intent, and preserving both changes where possible. Runs automated checks after resolution.