Skill
Write a silver-castle SQL migration — idempotency law (they re-run every boot), numbering by directory listing, new-table boilerplate, RLS/permissions in-file.
Playbook (mirrored from disk)
DB Migration
The law
Migrations in apps/api/db/migrations/NNN_description.sql re-run on EVERY api container boot —
there is NO applied-ledger. Every statement must be idempotent or every future deploy breaks:
create table if not exists·create index if not exists·insert ... on conflict do nothing- Policies wrapped:
do $$ begin
if not exists (select 1 from pg_policies where policyname = 'p_name' and tablename = 't') then
create policy p_name on t ...;
end if;
end $$;Numbering
ls apps/api/db/migrations/ | sort → take max(NNN)+1. NEVER number from memory — a duplicate
036 already exists as a monument to that mistake.
File shape
Header comment: -- NNN — description + intent + end-state + the word Idempotent.
New tables: id uuid primary key default gen_random_uuid(), FKs
references sc_profiles(id) on delete cascade, created_at timestamptz not null default now(),
enable row level security, named indexes sc_<table>_<col>_idx / _uniq.
Permission/RLS seed rows ship in the SAME file as the feature’s table.
Boundaries
sc_* tables only — the Supabase is shared with SmartBudget + admin. Never touch auth./storage.
schemas. RLS via the sc_can() helper for browser-direct paths.
Verification: re-read the file asking “what happens on the SECOND run?” — then api typecheck+test.