> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cfosilvia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Supabase ci migrations

# Supabase migrations: native GitHub integration

Supabase's GitHub integration deploys `supabase/migrations/`. There is no
migration workflow of our own.

| Git branch | Supabase branch | When it deploys |
| - | - | - |
| PR branch | preview branch (per PR) | on push that changes Supabase files |
| `staging` | `staging` (persistent, `evmxqxlurxfhzvwjxqrf`) | on merge |
| `main` | production (`byoneafwuigeyehkoacw`) | on merge ("Deploy to production") |

Flow: PR into `staging` (preview branch builds the schema from the files) ->
merge (applies to staging) -> release PR `staging` -> `main` -> merge (applies
to production).

## Layout (since 2026-10-16)

* `supabase/migrations/20261016205500_prod_schema_baseline.sql`: production's
  schema, dumped 2026-10-16 20:55 UTC with the `supabase db dump` recipe. Every
  branch starts here.
* `supabase/migrations/` after the baseline: migrations not yet in production.
* `supabase/migrations-archive/`: every earlier file, original names. Supabase
  ignores this folder. Tests read both folders through
  `lib/migrations/__tests__/migration-files.ts`, so guard tests still check
  every new migration.

### Why a baseline

The old files could not rebuild the database. Tables (`ai_token_usage_daily`,
`api_token_usage`, `import_agent_runs`), functions and a role
(`analytics_read`) were created by hand and never committed, 78 dates had two
or more files sharing one version, and some files ran in the wrong order. Every
Supabase branch build failed, and the integration has reported
`MIGRATIONS_FAILED` on staging and main for months.

Left out of the baseline on purpose: Supabase-managed schemas, scratch schemas
(`cfo_scratch`, `repair_ops`), one-off backup/recon tables, event triggers and
pg\_cron schedules (data, not schema). The baseline resets Supabase's default
grants before restoring production's, so branches get production's privileges.

## Rules for new migrations

* **Unique 14-digit version.** `npx supabase migration new <name>`. CI runs
  `scripts/check-migration-versions.mjs` in the lint job.
* **No `CONCURRENTLY`; wrap `LOCK TABLE` in `BEGIN`/`COMMIT`.** Supabase's
  runner sends a file as one batch. For an index on a big production table,
  build it `CONCURRENTLY` by hand first, then merge a plain
  `CREATE INDEX IF NOT EXISTS`.
* **Never change a database by hand.** A recorded version with no file stops
  every later deploy, and the change is lost the next time a branch is built.

## Cutover

1. PR preview branch builds green from the baseline.
2. Staging: back up `supabase_migrations.schema_migrations`, then record the
   baseline and the files after it as applied (they are already on staging).
3. Merge the PR to `staging`; the integration should report staging up to date.
4. Production (separately approved): back up and record the baseline as
   applied. The files after it are not in production yet; the next release
   applies them.
5. Supabase dashboard: production branch `main` with "Deploy to production",
   staging persistent branch on git `staging`, automatic branching "Supabase
   changes only". GitHub: make `Supabase Preview` a required check.

## Known drift, to fix later

* **Staging is ahead of production.** Migrations on `main` that production never
  got (the multi-portfolio backend 20260928\_06..20261008, the snaptrade webhook
  and vault\_folders unique indexes, the ai\_chat\_logs FTS indexes) are in the
  archive but missing from production and the baseline. Staging has them.
* **Staging also differs** from production on policies, grants and functions
  (staging: 445 RLS policies, production: 640).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.