Supabase Safe Migration
Activation scope
Use this skill when a task touches:
- tables, columns, enums, constraints, or indexes
- migrations or schema drift
- RLS policies or permission helpers
- RPC functions, triggers, or SQL helpers
- seed data
- Supabase Storage policies
- database-backed integration tests
- race-condition-sensitive state
- Supabase CLI commands such as
db reset, db diff, db push, or migration repair
Do not use it for pure UI work with no database dependency.
Database behavior changes must be represented by migrations, not ad hoc dashboard SQL.
Never push migrations or modify a remote database unless the user explicitly requests it.
Related skills
Use:
nextjs-server-action-zod when the requested work changes or reviews a Server Action, Route Handler, RPC argument/payload contract, form, schema, or DTO; inspecting an unchanged caller or supplied RPC payload only as migration evidence does not activate it
test-quality-strategy for database, RLS, RPC, migration, trigger, and concurrency coverage
frontend-workflow when seed data or fixtures are intended for browser or frontend QA, even when no UI code changes
code-commenting-and-maintainability for non-obvious SQL and database-boundary comments
implementation-planning-and-pr-breakdown for multi-step migration or dependency ordering
git-checkpoint-workflow for local checkpoint commits
Read all relevant skills before editing.
Resource routing
| Resource |
Read condition |
Skip when |
| references/migration-and-seed.md |
Read before adding/reviewing a migration, schema/table/column/index/constraint/backfill, or seed change |
RLS/RPC/trigger/Storage investigation with no migration/seed review or change |
| references/rls-and-storage.md |
Read before changing/reviewing RLS policies, permission helpers, bucket access, or Storage policies |
Schema-only/RPC-only/trigger-only work, including an event or trigger that merely enables RLS without reviewing or changing an RLS policy, permission helper, or Storage access |
| references/rpc-trigger-concurrency.md |
Read when the requested outcome explicitly adds, changes, or reviews an RPC, trigger, SQL helper, race-sensitive transition, lock, retry, or idempotency behavior |
General migration/constraint work where those objects appear only inside supplied SQL or tests; additive schema/index/seed work without a requested behavior target |
Read every reference whose condition matches the outcome or invariant the task explicitly asks to change or review. For RPC routing, a request merely to review or locally verify a supplied migration remains general migration scope: do not activate rpc-trigger-concurrency.md because its SQL creates or replaces an RPC or contains lock or concurrency implementation details. Read that reference when the request or owner-stated invariant explicitly names the RPC, trigger, concurrency, locking, retry, or idempotency behavior to add, change, or review. A remote-push-only request without a procedure trigger uses the core permission stop.
Core rules
- Read existing migrations, schema objects, policies, helpers, triggers, RPCs, tests, seed data, and call sites first.
- Make the smallest focused database change.
- Create a new migration unless the user explicitly asks to amend unpublished local work.
- Do not edit old published migrations.
- Do not weaken RLS or constraints to make code or tests pass.
- Reuse existing helpers and project patterns.
- Treat RLS as a security boundary and constraints as final integrity enforcement.
- Do not rely only on TypeScript or client validation for database invariants.
- Prefer additive, existing-data-safe steps.
- Before deciding an ordered soft-delete backfill or constraint change, identify active display ordering, deleted-state retention, and restore conflict semantics from authoritative ADRs, mutation paths, and tests. Keep these domains separate unless the product contract explicitly joins them; stop when their contract or intended order cannot be established.
- Keep locks and transactions short.
- Do not call external services while holding database locks.
- Preserve idempotency for retryable payment, webhook, and status-transition logic.
- Surface schema, permission, or data-model conflicts before editing.
- Do not hide business-rule changes inside SQL without reviewing schemas, actions, tests, and seed assumptions.
- Do not modify unrelated database objects.
- Never run
db push without explicit permission.
Specialist escalation signals
A hard-risk signal exists when observable database facts expose a potentially material unresolved uncertainty about an RLS or permission boundary; SECURITY DEFINER or search_path; a destructive or compatibility-sensitive migration, backfill, or constraint on existing data; a transaction, lock, concurrency, or idempotency invariant; a trigger side effect; or a permission-sensitive RPC. Group signals by the threatened data or authorization invariant.
Additive migrations with known compatibility, mechanical generated-type updates, reuse of an established policy or RPC pattern, and ordinary local reset or drift checks are conditional review signals unless evidence exposes unresolved material risk. Documentation-only changes, file count, Supabase activation, or a request to double-check are ordinary non-triggers.
Route a hard-risk candidate through the global specialist gates only after applicable main review. Existing migration, RLS, remote-database, destructive-action, verification, and stop rules remain authoritative; no signal grants database, implementation, or remote permission.
Required workflow
Before editing
- Identify the affected database behavior and business invariant.
- Inspect relevant migrations and current schema objects.
- Inspect related:
- tables and relationships
- constraints and indexes
- RLS policies and helper functions
- RPCs, triggers, and SQL helpers
- Storage policies
- seed data
- TypeScript/Zod/action/handler call sites
- integration tests
- Search for an existing pattern or helper that already fits.
- Decide whether the task needs:
- a migration
- seed changes
- schema/type or call-site changes
- RLS/RPC/trigger tests
- local reset or drift checks
- Surface conflicts before writing SQL.
While editing
- Keep one migration focused on the approved behavior.
- Preserve naming and helper patterns.
- Apply changes in an existing-data-safe order.
- Keep permission checks and invariants explicit.
- Update application call sites only when required.
- Add concise Vietnamese comments only for non-obvious ordering, RLS, atomicity, lock, trigger, backfill, or rollback reasoning.
- Avoid unrelated refactors.
After editing
- Apply the migration locally.
- Check drift when relevant.
- Run the smallest relevant test set.
- Confirm RLS/helper/trigger patterns were reused or intentionally changed.
- Confirm no unrelated schema object changed.
- Report exact changes, commands, results, skipped checks, and pending manual QA.
Permission helper rules
Use explicit restrictive behavior when hard delete must be blocked.
Do not create a new permission helper until existing helpers are proven insufficient and the new boundary is approved.
Trigger scope
Do not:
- create another updated-at helper unnecessarily
- modify managed schemas such as
auth, storage, cron, or realtime without explicit scope
- touch auth provisioning triggers unless the task is specifically about provisioning
Storage safety
Do not broaden Storage access casually.
RPC rules
Use RPC when an operation must be atomic, permission-sensitive, or concurrency-safe at the database layer.
Before creating or changing one:
- Inspect existing RPC and helper patterns.
- Decide whether
SECURITY DEFINER is necessary.
- Set a safe
search_path when using SECURITY DEFINER.
- Check actor permission and current state explicitly.
- Make retryable transitions idempotent.
- Avoid duplicate side effects.
- Return only required data.
- Update callers and tests when needed.
Do not create RPC merely to hide ordinary CRUD.
Database-specific comments
Follow code-commenting-and-maintainability.
Comment only non-obvious:
- safe migration order
- existing-data backfill
- RLS boundary
- trigger/helper purpose
- lock or atomic update
- partial constraint/index
SECURITY DEFINER or search_path
- retry/idempotency invariant
- rollback-sensitive behavior
Good:
-- Backfill trước khi thêm NOT NULL để migration chạy được trên database đã có dữ liệu.
Do not narrate ordinary SQL syntax.
Verification matrix
Choose the smallest relevant set.
Use the matching verification details in the selected references.
Inspect package.json and Supabase config before choosing exact commands.
Typical commands may include:
npx supabase db reset
npx supabase db diff --schema public
npm run test:integration
npm run lint
npm run typecheck
Do not invent unavailable scripts.
A DB-backed integration change is not complete until the local database can be rebuilt successfully.
When smoke E2E uses an isolated Supabase workdir/runtime, migrations and RPCs needed by E2E must be applied there too, not only to the root local Supabase database. If E2E reports a missing function after migration work, check both the root DB and the E2E DB/workdir; reset the E2E workdir locally when it is stale, then restart PostgREST/schema cache only after confirming the function exists. Never apply this troubleshooting flow to production without explicit owner approval.
Never run npx supabase db push without explicit user permission.
Anti-patterns
Do not:
- edit published migrations
- use dashboard SQL as the final change
- weaken RLS or constraints
- duplicate permission helpers
- create redundant updated-at functions
- trust client-provided owner, role, status, price, payment, or counter fields
- hold locks during external calls
- make retryable operations non-idempotent
- skip reset after migration changes
- ignore unexplained schema drift
- change unrelated tables, policies, or triggers
- claim remote application when only local checks ran
Reporting
In the response, name selected and skipped bundled references and briefly tie the selection to their read conditions. For a core-only task, state that no conditional reference applies.
For each applicable database boundary under review, state the conclusion, the supporting evidence, any remaining unknown, and the smallest verification still needed. Distinguish checks that ran from checks that are only planned, and state why a boundary is not applicable instead of silently omitting it.
Final checklist
1---2name: supabase-safe-migration3description: Repository-specific workflow for Supabase/PostgreSQL changes, including migrations, tables, columns, constraints, indexes, RLS, RPC, triggers, SQL helpers, seed data, Storage policies, integration tests, race conditions, and database-dependent business behavior. Use before changing or relying on database behavior.4---56# Supabase Safe Migration78## Activation scope910Use this skill when a task touches:1112* tables, columns, enums, constraints, or indexes13* migrations or schema drift14* RLS policies or permission helpers15* RPC functions, triggers, or SQL helpers16* seed data17* Supabase Storage policies18* database-backed integration tests19* race-condition-sensitive state20* Supabase CLI commands such as `db reset`, `db diff`, `db push`, or migration repair2122Do not use it for pure UI work with no database dependency.2324Database behavior changes must be represented by migrations, not ad hoc dashboard SQL.2526Never push migrations or modify a remote database unless the user explicitly requests it.2728## Related skills2930Use:3132* `nextjs-server-action-zod` when the requested work changes or reviews a Server Action, Route Handler, RPC argument/payload contract, form, schema, or DTO; inspecting an unchanged caller or supplied RPC payload only as migration evidence does not activate it33* `test-quality-strategy` for database, RLS, RPC, migration, trigger, and concurrency coverage34* `frontend-workflow` when seed data or fixtures are intended for browser or frontend QA, even when no UI code changes35* `code-commenting-and-maintainability` for non-obvious SQL and database-boundary comments36* `implementation-planning-and-pr-breakdown` for multi-step migration or dependency ordering37* `git-checkpoint-workflow` for local checkpoint commits3839Read all relevant skills before editing.4041## Resource routing4243| Resource | Read condition | Skip when |44| --- | --- | --- |45| [references/migration-and-seed.md](references/migration-and-seed.md) | Read before adding/reviewing a migration, schema/table/column/index/constraint/backfill, or seed change | RLS/RPC/trigger/Storage investigation with no migration/seed review or change |46| [references/rls-and-storage.md](references/rls-and-storage.md) | Read before changing/reviewing RLS policies, permission helpers, bucket access, or Storage policies | Schema-only/RPC-only/trigger-only work, including an event or trigger that merely enables RLS without reviewing or changing an RLS policy, permission helper, or Storage access |47| [references/rpc-trigger-concurrency.md](references/rpc-trigger-concurrency.md) | Read when the requested outcome explicitly adds, changes, or reviews an RPC, trigger, SQL helper, race-sensitive transition, lock, retry, or idempotency behavior | General migration/constraint work where those objects appear only inside supplied SQL or tests; additive schema/index/seed work without a requested behavior target |4849Read every reference whose condition matches the outcome or invariant the task explicitly asks to change or review. For RPC routing, a request merely to review or locally verify a supplied migration remains general migration scope: do not activate `rpc-trigger-concurrency.md` because its SQL creates or replaces an RPC or contains lock or concurrency implementation details. Read that reference when the request or owner-stated invariant explicitly names the RPC, trigger, concurrency, locking, retry, or idempotency behavior to add, change, or review. A remote-push-only request without a procedure trigger uses the core permission stop.5051## Core rules5253* Read existing migrations, schema objects, policies, helpers, triggers, RPCs, tests, seed data, and call sites first.54* Make the smallest focused database change.55* Create a new migration unless the user explicitly asks to amend unpublished local work.56* Do not edit old published migrations.57* Do not weaken RLS or constraints to make code or tests pass.58* Reuse existing helpers and project patterns.59* Treat RLS as a security boundary and constraints as final integrity enforcement.60* Do not rely only on TypeScript or client validation for database invariants.61* Prefer additive, existing-data-safe steps.62* Before deciding an ordered soft-delete backfill or constraint change, identify active display ordering, deleted-state retention, and restore conflict semantics from authoritative ADRs, mutation paths, and tests. Keep these domains separate unless the product contract explicitly joins them; stop when their contract or intended order cannot be established.63* Keep locks and transactions short.64* Do not call external services while holding database locks.65* Preserve idempotency for retryable payment, webhook, and status-transition logic.66* Surface schema, permission, or data-model conflicts before editing.67* Do not hide business-rule changes inside SQL without reviewing schemas, actions, tests, and seed assumptions.68* Do not modify unrelated database objects.69* Never run `db push` without explicit permission.7071## Specialist escalation signals7273A hard-risk signal exists when observable database facts expose a potentially material unresolved uncertainty about an RLS or permission boundary; `SECURITY DEFINER` or `search_path`; a destructive or compatibility-sensitive migration, backfill, or constraint on existing data; a transaction, lock, concurrency, or idempotency invariant; a trigger side effect; or a permission-sensitive RPC. Group signals by the threatened data or authorization invariant.7475Additive migrations with known compatibility, mechanical generated-type updates, reuse of an established policy or RPC pattern, and ordinary local reset or drift checks are conditional review signals unless evidence exposes unresolved material risk. Documentation-only changes, file count, Supabase activation, or a request to double-check are ordinary non-triggers.7677Route a hard-risk candidate through the global specialist gates only after applicable main review. Existing migration, RLS, remote-database, destructive-action, verification, and stop rules remain authoritative; no signal grants database, implementation, or remote permission.7879## Required workflow8081### Before editing82831. Identify the affected database behavior and business invariant.842. Inspect relevant migrations and current schema objects.853. Inspect related:86 * tables and relationships87 * constraints and indexes88 * RLS policies and helper functions89 * RPCs, triggers, and SQL helpers90 * Storage policies91 * seed data92 * TypeScript/Zod/action/handler call sites93 * integration tests944. Search for an existing pattern or helper that already fits.955. Decide whether the task needs:96 * a migration97 * seed changes98 * schema/type or call-site changes99 * RLS/RPC/trigger tests100 * local reset or drift checks1016. Surface conflicts before writing SQL.102103### While editing104105* Keep one migration focused on the approved behavior.106* Preserve naming and helper patterns.107* Apply changes in an existing-data-safe order.108* Keep permission checks and invariants explicit.109* Update application call sites only when required.110* Add concise Vietnamese comments only for non-obvious ordering, RLS, atomicity, lock, trigger, backfill, or rollback reasoning.111* Avoid unrelated refactors.112113### After editing114115* Apply the migration locally.116* Check drift when relevant.117* Run the smallest relevant test set.118* Confirm RLS/helper/trigger patterns were reused or intentionally changed.119* Confirm no unrelated schema object changed.120* Report exact changes, commands, results, skipped checks, and pending manual QA.121122## Permission helper rules123124Use explicit restrictive behavior when hard delete must be blocked.125126Do not create a new permission helper until existing helpers are proven insufficient and the new boundary is approved.127128## Trigger scope129130Do not:131132* create another updated-at helper unnecessarily133* modify managed schemas such as `auth`, `storage`, `cron`, or `realtime` without explicit scope134* touch auth provisioning triggers unless the task is specifically about provisioning135136## Storage safety137138Do not broaden Storage access casually.139140## RPC rules141142Use RPC when an operation must be atomic, permission-sensitive, or concurrency-safe at the database layer.143144Before creating or changing one:1451461. Inspect existing RPC and helper patterns.1472. Decide whether `SECURITY DEFINER` is necessary.1483. Set a safe `search_path` when using `SECURITY DEFINER`.1494. Check actor permission and current state explicitly.1505. Make retryable transitions idempotent.1516. Avoid duplicate side effects.1527. Return only required data.1538. Update callers and tests when needed.154155Do not create RPC merely to hide ordinary CRUD.156157## Database-specific comments158159Follow `code-commenting-and-maintainability`.160161Comment only non-obvious:162163* safe migration order164* existing-data backfill165* RLS boundary166* trigger/helper purpose167* lock or atomic update168* partial constraint/index169* `SECURITY DEFINER` or `search_path`170* retry/idempotency invariant171* rollback-sensitive behavior172173Good:174175```sql176-- Backfill trước khi thêm NOT NULL để migration chạy được trên database đã có dữ liệu.177```178179Do not narrate ordinary SQL syntax.180181## Verification matrix182183Choose the smallest relevant set.184185Use the matching verification details in the selected references.186187Inspect `package.json` and Supabase config before choosing exact commands.188189Typical commands may include:190191```bash192npx supabase db reset193npx supabase db diff --schema public194npm run test:integration195npm run lint196npm run typecheck197```198199Do not invent unavailable scripts.200201A DB-backed integration change is not complete until the local database can be rebuilt successfully.202203When smoke E2E uses an isolated Supabase workdir/runtime, migrations and RPCs needed by E2E must be applied there too, not only to the root local Supabase database. If E2E reports a missing function after migration work, check both the root DB and the E2E DB/workdir; reset the E2E workdir locally when it is stale, then restart PostgREST/schema cache only after confirming the function exists. Never apply this troubleshooting flow to production without explicit owner approval.204205Never run `npx supabase db push` without explicit user permission.206207## Anti-patterns208209Do not:210211* edit published migrations212* use dashboard SQL as the final change213* weaken RLS or constraints214* duplicate permission helpers215* create redundant updated-at functions216* trust client-provided owner, role, status, price, payment, or counter fields217* hold locks during external calls218* make retryable operations non-idempotent219* skip reset after migration changes220* ignore unexplained schema drift221* change unrelated tables, policies, or triggers222* claim remote application when only local checks ran223224## Reporting225226In the response, name selected and skipped bundled references and briefly tie the selection to their read conditions. For a core-only task, state that no conditional reference applies.227228For each applicable database boundary under review, state the conclusion, the supporting evidence, any remaining unknown, and the smallest verification still needed. Distinguish checks that ran from checks that are only planned, and state why a boundary is not applicable instead of silently omitting it.229230## Final checklist231232* [ ] Existing DB patterns and call sites were inspected233* [ ] A focused new migration represents the behavior234* [ ] Existing data remains valid235* [ ] RLS and constraints preserve intended boundaries236* [ ] RPC/trigger/lock behavior is justified237* [ ] Retryable behavior is idempotent238* [ ] Relevant application contracts remain compatible239* [ ] Reset and relevant tests passed240* [ ] Drift was checked when needed241* [ ] No unrelated DB object changed242* [ ] Comments explain only non-obvious decisions243* [ ] Remote DB was not modified without explicit permission244* [ ] Exact verification and limitations were reported