Skip to content

Implementation Log: ANU-7 - Migrate Supabase Postgres data and verify local cutover

Changes and decisions

  • 2026-07-21: Validated ANU-7 as Ready for anuvax-cms; dependency ANU-6 was Done and Product Main scope, acceptance, docs, and verification inputs were complete.
  • 2026-07-21: Started the issue through anuva work start; Linear moved to In Progress and the dedicated implementation branch was created from main.
  • 2026-07-21: Kept the accepted ANU-6 evidence immutable and created a separate ANU-7 change record for the stateful migration and cutover.
  • 2026-07-21: Pinned Supabase CLI 2.109.1 as a project dev dependency, following the official project-local installation model. Added source and combined source/local wrapper commands that keep connection strings in process environment only.
  • 2026-07-21: Extended the guarded database tool with read-only inventory, reviewed schema selection, CLI dry runs, component-level credential redaction, checksum verification, local schema preparation, fresh target creation, empty-target extension alignment, transactional restore, semantic parity, and targeted cutover probes.
  • 2026-07-21: Source inventory ran in a read-only transaction against PostgreSQL 17.6. It found about 52 MB, 137 non-system tables overall, 102 application tables in public, Payload migrations, 27 Mastra tables, RAG tables, and the application vector index. Supabase-managed schemas and roles were recorded by name only and excluded from restore.
  • 2026-07-21: The first CLI dry run exposed a credential assignment in its raw diagnostic output. Deleted that ignored dry-run directory, expanded redaction to full URI and decoded/encoded username/password components, then repeated the dry run successfully with no URI or credential component in the log.
  • 2026-07-21: Exported only public schema and data with Supabase CLI 2.109.1. The schema was 206,471 bytes and data was 36,471,404 bytes. SHA-256 verification, .gitignore checks, and Git absence checks passed.
  • 2026-07-21: Prepared a local schema without importing hosted roles. The deterministic preparation removed 178 ownership statements, 340 grants, and 12 default-privilege statements; it removed no table, constraint, index, or data statement and found no residual hosted-role reference.
  • 2026-07-21: Preserved the accepted anuvax database and created a separate anuvax_anu7 database in the same named volume. The first direct create attempt failed at host-shell parsing before mutation; the guarded TypeScript operation then created the database successfully.
  • 2026-07-21: The first restore transport hit a Windows spawnSync EOF while piping the 36 MB dump. The target remained empty. Changed restore transport so the web container reads bind-mounted ignored files directly with psql --single-transaction.
  • 2026-07-21: The second restore rolled back because the portable schema references public.vector while the first target setup installed pgvector in extensions. The target again remained empty. Moved pgvector to public only on that empty database and changed future target creation accordingly.
  • 2026-07-21: The third restore committed successfully. No role import, managed schema import, blanket public grant, or privilege weakening occurred.
  • 2026-07-21: Fixed parity-query implementation issues exposed before a report was accepted: session temporary tables now precede the read-only snapshot, a PostgreSQL char signature field is cast explicitly, and JSON objects are canonicalized before semantic comparison.
  • 2026-07-21: Final parity passed with 102 table counts, 99 primary-key hashes, 283 constraint signatures, 482 index signatures, nine sequence states, workspace-null counts, Payload state, 27 Mastra tables, and RAG/vector state equal between source and target. PostgreSQL 17.6 to 17.10 and pgvector 0.8.0 to 0.8.2 were retained as reviewed platform patch differences.
  • 2026-07-21: Targeted probes found zero presentation or RAG workspace mismatches, four Payload migration rows, 51 Mastra workflow snapshots, 786 RAG chunks, 786 migrated vectors, the migrated vector index, and one successful nearest-neighbor result. All three job queues were empty.
  • 2026-07-21: Switched only ignored .env.compose POSTGRES_DB to anuvax_anu7. Compose recreated all services healthy; the web container resolved the local target and integrated Payload, Mastra, RAG, and worker API smoke passed.
  • 2026-07-21: Payload migration status reported all three checked-in migrations applied. Public, Payload admin, login, and pgAdmin endpoints returned HTTP 200. RAG, presentation, and Anuva workers each passed bounded startup probes while all queues were empty.
  • 2026-07-21: Restart and routine down/up preserved the target. The full application smoke passed after both cycles, and the aggregate targeted probe matched exactly after volume reattachment.
  • 2026-07-21: Demonstrated rollback connectivity by forwarding the untouched Supabase URI from ignored .env into a separate PAYLOAD_DB_PUSH=0 Payload migration-status process. It reported all checked-in migrations applied. The running web service remained on postgres/anuvax_anu7, and the local volume was not deleted.
  • 2026-07-21: Acceptance review found and corrected two repeatability gaps. Fresh target creation now derives its administrative connection from the always-present loopback postgres database instead of trying to connect to the not-yet-created target, and restore paths are passed as positional shell arguments instead of interpolated shell source. Parity now exits nonzero after writing its ignored report when application state differs. Added regression coverage; focused Biome, 17 guard tests, and typecheck passed.
  • 2026-07-21: Acceptance review also brought the required verification harness back to the current implementation contract. The orchestration assertion now names the split narrative and presentation-analysis actions. Playwright now starts from a clean .next build, waits for route hydration, starts directly at Video Setup, uses the current assistant label, and compiles the slides preview before expecting render readiness. The full integration/unit suite passed 103 tests, and all six Chromium presentation flows passed against the migrated local database. The test server still emits non-fatal Better Auth 401 diagnostics while its explicit E2E bypass is active.
  • 2026-07-21: The repository-wide pnpm lint rerun remained failed with exit code 1 after checking 441 files. Biome 2.3.9 reported exactly six errors and six warnings across five files unchanged by ANU-7: formatter errors in docs/_stale/archive/globals.css, docs/stylesheets.css, and src/payload-types.ts; organize-imports errors in src/app/(payload)/admin/importMap.js and src/mastra/tools/presentation/index.ts; one additional formatter error in src/app/(payload)/admin/importMap.js; and six noImportantStyles warnings in docs/stylesheets.css. The repository runs Biome 2.3.9, unchanged from main; its package specification, lock entry, and biome.json configuration are unchanged. These findings were accepted as an out-of-scope baseline exception for ANU-7 only; the full lint result remains recorded as failed and the unrelated files were not modified.
  • 2026-07-21: After recording the exception, the focused Biome check passed all six ANU-7 TypeScript/config/test files, typecheck passed, the integration/unit suite passed all 103 tests, and the Chromium project passed all six flows. Recursive documentation index validation found no missing or empty indexes or directories, and the strict MkDocs build passed.

Files and documentation updated

  • package.json and pnpm-lock.yaml for pinned Supabase CLI 2.109.1 and local database wrapper commands.
  • scripts/local-stack/database.ts, database-migrate.ts, inventory.sql, parity-summary.sql, and cutover-verification.sql for guarded migration and redacted verification behavior.
  • tests/unit/local-stack.test.ts for inventory plan mode, schema validation, checksum tamper detection, schema preparation, and target-creation guards.
  • Local-stack runbook plus testing, environment, command, and known-gap docs.
  • ANU-7 change index, plan, log, completion report, recursive change index, and MkDocs navigation.

Verification evidence

  • Supabase CLI 2.109.1 version and dump help: passed.
  • Source inventory, CLI dump dry run, public schema/data exports, checksum verification, credential scan, ignore checks, and Git absence: passed.
  • Prepared-schema review and fresh-target/empty-target guards: passed.
  • Transactional restore and semantic parity: passed.
  • Targeted workspace/Payload/Mastra/RAG/vector/queue probe: passed.
  • Compose cutover, all service health, stack:verify, Payload migration status, HTTP surface probes, bounded worker startup, restart, and down/up persistence: passed.
  • Supabase rollback-connectivity demonstration with schema push disabled: passed.
  • Focused Biome check, TypeScript typecheck, and 17 local-stack guard tests: passed.
  • Repository-wide pnpm lint: failed with exit code 1; exact accepted baseline was six errors and six warnings across five unchanged files under Biome 2.3.9.
  • Recursive documentation index validation and strict MkDocs build: passed.

Deviations, risks, and follow-ups

  • Repository-wide pnpm lint remains failed with six errors and six warnings across the five unchanged generated/historical and source files recorded above. Biome 2.3.9, its package specification, lock entry, and configuration are unchanged from main. This is an ANU-7-only baseline exception, not a passing lint result; all ANU-7 files pass focused lint.
  • The required integration/unit and Chromium suites pass. Better Auth 401 diagnostics still appear in the mocked E2E server output even though all six browser flows complete; cleaning up that non-fatal harness noise is separate from database parity and cutover behavior.
  • Source writes were not frozen product-wide, so acceptance proves a consistent logical snapshot at export time rather than continuous replication after cutover. Future refreshes must repeat export and parity.