active
SkillSlap Migration Writer
Safe
System VerifiedSafe
Generate production-ready Supabase migration SQL following SkillSlap conventions: RLS policies, audit triggers, idempotent guards, and rollback safety. Give it a feature description and get a migration file back.
@system/skillslap-migration-writer
supabase
sql
migration
rls
postgres
skillslap
SkillSlap Migration Writer
Purpose: Generate a complete, production-ready Supabase migration SQL file following SkillSlap's exact conventions. No hand-editing the output — it should be paste-and-push quality.
Invocation
code
/migration <feature-description>
Examples:
/migration Add a skill_reports table so users can flag inappropriate skills/migration Add a focus_pool_history table tracking daily focus_pool snapshots per skill/migration Add a skill_collections table for users to curate skill lists
SkillSlap Migration Conventions
File naming
YYYYMMDDHHMMSS_snake_case_description.sql
Use the current timestamp. Keep description to 3–5 words max.
Required header
sql
-- Migration: [description]
-- Created: [date]
-- Purpose: [one sentence]
Idempotency — always guard with IF NOT EXISTS
sql
-- Tables
CREATE TABLE IF NOT EXISTS public.[table] (...);
-- Columns
ALTER TABLE public.[table]
ADD COLUMN IF NOT EXISTS [col] [type];
-- Indexes
CREATE INDEX IF NOT EXISTS idx_[table]_[col] ON public.[table]([col]);
Column conventions
sql
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
Always add an updated_at trigger:
sql
CREATE TRIGGER [table]_updated_at
BEFORE UPDATE ON public.[table]
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Note: update_updated_at_column() already exists in SkillSlap schema
Foreign keys
sql
skill_id uuid NOT NULL REFERENCES public.skills(id) ON DELETE CASCADE,
user_id uuid NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
Always use ON DELETE CASCADE for child records unless there's a specific reason for SET NULL or RESTRICT.
RLS — every user-data table must have it
sql
ALTER TABLE public.[table] ENABLE ROW LEVEL SECURITY;
-- SELECT: authenticated users see their own rows
CREATE POLICY "[table]_select_own" ON public.[table]
FOR SELECT TO authenticated
USING (auth.uid() = user_id);
-- SELECT: public data (active status)
CREATE POLICY "[table]_select_public" ON public.[table]
FOR SELECT USING (status = 'active');
-- INSERT: user can only insert their own rows
CREATE POLICY "[table]_insert_own" ON public.[table]
FOR INSERT TO authenticated
WITH CHECK (auth.uid() = user_id);
-- UPDATE: user can only update their own rows
CREATE POLICY "[table]_update_own" ON public.[table]
FOR UPDATE TO authenticated
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- DELETE: user can only delete their own rows
CREATE POLICY "[table]_delete_own" ON public.[table]
FOR DELETE TO authenticated
USING (auth.uid() = user_id);
-- Service role bypass (only if needed for server-side writes)
-- Document WHY this is needed
CREATE POLICY "[table]_service_role" ON public.[table]
USING (auth.role() = 'service_role');
Enum types
sql
CREATE TYPE public.[name]_status AS ENUM ('pending', 'active', 'archived');
-- Add IF NOT EXISTS guard using a named block for types
DO $block$ BEGIN
CREATE TYPE public.[name]_status AS ENUM ('pending', 'active', 'archived');
EXCEPTION WHEN duplicate_object THEN NULL;
END $block$;
Indexes — add for every foreign key and common query pattern
sql
CREATE INDEX IF NOT EXISTS idx_[table]_user_id ON public.[table](user_id);
CREATE INDEX IF NOT EXISTS idx_[table]_skill_id ON public.[table](skill_id);
CREATE INDEX IF NOT EXISTS idx_[table]_created_at ON public.[table](created_at DESC);
-- Partial index for active rows only (common pattern)
CREATE INDEX IF NOT EXISTS idx_[table]_active ON public.[table](created_at DESC)
WHERE status = 'active';
GRANTs
sql
GRANT SELECT, INSERT, UPDATE, DELETE ON public.[table] TO authenticated;
GRANT SELECT ON public.[table] TO anon; -- only if table has public data
Output Format
- Complete migration file — ready to save as
supabase/migrations/[timestamp]_[name].sql - Down migration — save as
supabase/migrations/down/[same-filename].sql - Summary — bullet list of what the migration does (for commit message and PR)
Down migration pattern
sql
-- Down: [description]
DROP TABLE IF EXISTS public.[table] CASCADE;
DROP TYPE IF EXISTS public.[type];
Checklist Before Outputting
- All tables have
id uuid PRIMARY KEY DEFAULT gen_random_uuid() - All tables have
created_at+updated_atwith trigger - All foreign keys have
ON DELETE CASCADEor explicit reason for alternative - RLS enabled and all CRUD policies present for tables with user data
- Indexes on all foreign keys
- Idempotent (IF NOT EXISTS on all DDL)
- Down migration included
- GRANTs match data sensitivity (no anon grant on private tables)
Dormant$0/mo
$20 more to next tier
Created by
Info
Created February 24, 2026
Version 1.0.0
Agent-invoked
Terminal output
Embed
Add this skill card to any webpage.
<iframe src="https://skillslap.com/skill/68871c4f-f65b-4812-b37c-003d2477561b/embed"
width="400" height="200"
style="border:none;border-radius:12px;"
title="SkillSlap Skill: SkillSlap Migration Writer">
</iframe>