{"manifest":{"name":"SkillSlap Migration Writer","version":"1.0.0","description":"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.","tags":["supabase","sql","migration","rls","postgres","skillslap"],"standard":"agentskills.io","standard_version":"1.0","content_checksum":"ea03b9aeb63b10dced6e6414a1d61ef6c53547da644866a024db3008b485a24f","bundle_checksum":null,"metadata":{},"files":[]},"files":{"SKILL.md":"# SkillSlap Migration Writer\n\n> **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.\n\n---\n\n## Invocation\n\n```\n/migration <feature-description>\n```\n\n**Examples:**\n- `/migration Add a skill_reports table so users can flag inappropriate skills`\n- `/migration Add a focus_pool_history table tracking daily focus_pool snapshots per skill`\n- `/migration Add a skill_collections table for users to curate skill lists`\n\n---\n\n## SkillSlap Migration Conventions\n\n### File naming\n`YYYYMMDDHHMMSS_snake_case_description.sql`\nUse the current timestamp. Keep description to 3–5 words max.\n\n### Required header\n```sql\n-- Migration: [description]\n-- Created: [date]\n-- Purpose: [one sentence]\n```\n\n### Idempotency — always guard with IF NOT EXISTS\n```sql\n-- Tables\nCREATE TABLE IF NOT EXISTS public.[table] (...);\n\n-- Columns\nALTER TABLE public.[table]\n  ADD COLUMN IF NOT EXISTS [col] [type];\n\n-- Indexes\nCREATE INDEX IF NOT EXISTS idx_[table]_[col] ON public.[table]([col]);\n```\n\n### Column conventions\n```sql\nid          uuid PRIMARY KEY DEFAULT gen_random_uuid(),\ncreated_at  timestamptz NOT NULL DEFAULT now(),\nupdated_at  timestamptz NOT NULL DEFAULT now()\n```\n\nAlways add an `updated_at` trigger:\n```sql\nCREATE TRIGGER [table]_updated_at\n  BEFORE UPDATE ON public.[table]\n  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();\n-- Note: update_updated_at_column() already exists in SkillSlap schema\n```\n\n### Foreign keys\n```sql\nskill_id    uuid NOT NULL REFERENCES public.skills(id) ON DELETE CASCADE,\nuser_id     uuid NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,\n```\n\nAlways use `ON DELETE CASCADE` for child records unless there's a specific reason for `SET NULL` or `RESTRICT`.\n\n### RLS — every user-data table must have it\n```sql\nALTER TABLE public.[table] ENABLE ROW LEVEL SECURITY;\n\n-- SELECT: authenticated users see their own rows\nCREATE POLICY \"[table]_select_own\" ON public.[table]\n  FOR SELECT TO authenticated\n  USING (auth.uid() = user_id);\n\n-- SELECT: public data (active status)\nCREATE POLICY \"[table]_select_public\" ON public.[table]\n  FOR SELECT USING (status = 'active');\n\n-- INSERT: user can only insert their own rows\nCREATE POLICY \"[table]_insert_own\" ON public.[table]\n  FOR INSERT TO authenticated\n  WITH CHECK (auth.uid() = user_id);\n\n-- UPDATE: user can only update their own rows\nCREATE POLICY \"[table]_update_own\" ON public.[table]\n  FOR UPDATE TO authenticated\n  USING (auth.uid() = user_id)\n  WITH CHECK (auth.uid() = user_id);\n\n-- DELETE: user can only delete their own rows\nCREATE POLICY \"[table]_delete_own\" ON public.[table]\n  FOR DELETE TO authenticated\n  USING (auth.uid() = user_id);\n\n-- Service role bypass (only if needed for server-side writes)\n-- Document WHY this is needed\nCREATE POLICY \"[table]_service_role\" ON public.[table]\n  USING (auth.role() = 'service_role');\n```\n\n### Enum types\n```sql\nCREATE TYPE public.[name]_status AS ENUM ('pending', 'active', 'archived');\n-- Add IF NOT EXISTS guard using a named block for types\nDO $block$ BEGIN\n  CREATE TYPE public.[name]_status AS ENUM ('pending', 'active', 'archived');\nEXCEPTION WHEN duplicate_object THEN NULL;\nEND $block$;\n```\n\n### Indexes — add for every foreign key and common query pattern\n```sql\nCREATE INDEX IF NOT EXISTS idx_[table]_user_id ON public.[table](user_id);\nCREATE INDEX IF NOT EXISTS idx_[table]_skill_id ON public.[table](skill_id);\nCREATE INDEX IF NOT EXISTS idx_[table]_created_at ON public.[table](created_at DESC);\n-- Partial index for active rows only (common pattern)\nCREATE INDEX IF NOT EXISTS idx_[table]_active ON public.[table](created_at DESC)\n  WHERE status = 'active';\n```\n\n### GRANTs\n```sql\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.[table] TO authenticated;\nGRANT SELECT ON public.[table] TO anon;  -- only if table has public data\n```\n\n---\n\n## Output Format\n\n1. **Complete migration file** — ready to save as `supabase/migrations/[timestamp]_[name].sql`\n2. **Down migration** — save as `supabase/migrations/down/[same-filename].sql`\n3. **Summary** — bullet list of what the migration does (for commit message and PR)\n\n### Down migration pattern\n```sql\n-- Down: [description]\nDROP TABLE IF EXISTS public.[table] CASCADE;\nDROP TYPE IF EXISTS public.[type];\n```\n\n---\n\n## Checklist Before Outputting\n\n- [ ] All tables have `id uuid PRIMARY KEY DEFAULT gen_random_uuid()`\n- [ ] All tables have `created_at` + `updated_at` with trigger\n- [ ] All foreign keys have `ON DELETE CASCADE` or explicit reason for alternative\n- [ ] RLS enabled and all CRUD policies present for tables with user data\n- [ ] Indexes on all foreign keys\n- [ ] Idempotent (IF NOT EXISTS on all DDL)\n- [ ] Down migration included\n- [ ] GRANTs match data sensitivity (no anon grant on private tables)\n"}}