| name | create-migration |
| description | Scaffold a new Supabase SQL migration for toodaloo following the repo's house style (idempotent, RLS + GRANTs together, security-definer safety). Use when adding or changing database schema — a new table, column, RPC, view, policy, trigger, or constraint. Invoke with a short description of the change, e.g. "/create-migration reports table for content moderation".
|
| disable-model-invocation | true |
Create a Supabase migration
Migrations live in client/supabase/migrations/ as NNNN_short_name.sql. Follow this exactly — the conventions are load-bearing, not stylistic.
Steps
-
Find the next number. List client/supabase/migrations/, take the highest NNNN, add one, zero-pad to 4 digits. Never reuse or skip a number; never edit an already-applied file (append a new one instead).
-
Verify against the live schema before writing — never fabricate. Read the relevant existing migrations and, if the local stack is up, inspect the actual objects (via client/scripts/dev.sh / the /dev skill). Confirm real table names, column names/types, and function signatures before referencing them. Guessing an argument order or type is a hard "no" in this repo.
-
Write the file from the template below, keeping every statement idempotent.
-
If the change adds a new table, RPC, or view, you MUST add matching GRANTs (see the checklist) — RLS alone is not enough.
-
Apply locally and test. Reset/apply against the local Supabase stack (/dev skill), then run the app and/or npm test to confirm nothing regressed. Do not touch the paused production project (that's tracked separately in Linear).
-
Review before landing. Hand the diff to the migration-reviewer subagent.
Non-negotiable checklist
Template
create table if not exists public.<name> (
id uuid primary key default gen_random_uuid(),
created_at timestamptz not null default now()
);
alter table public.<name> enable row level security;
drop policy if exists "<name>_select" on public.<name>;
create policy "<name>_select" on public.<name>
for select using (<condition>);
drop policy if exists "<name>_insert_own" on public.<name>;
create policy "<name>_insert_own" on public.<name>
for insert with check (auth.uid() = <owner_col>);
grant select on public.<name> to anon, authenticated;
grant insert on public.<name> to authenticated;
Mirror the tone and structure of 0001_init.sql and 0004_grants.sql. When in doubt, read them.