CodeIssuesPull RequestsActionsSecurityInsights
✨ AI
More
Settings

fix(db): complete migration baseline (136 tables) + schema-drift CI guard #4073

Merged⚡ AI-generatedXSccantynz wants to mergeclaude/google-oauth-setup-UYn0Rmainopened Jun 11, 2026
ccantynzcommented Jun 11, 2026

Originally written by @ccantynz-alt on GitHub.
Imported from https://github.com/ccantynz-alt/AlecRae.com/pull/58


Problem

A fresh db:seed failed (42703 column "storage_used_bytes" ... does not exist), and diffing the schema against the migrations surfaced the real issue: migrations covered only 16 of 136 schema tables. A db:migrate-only fresh DB was missing 120 tables — including login-critical passkeys, refresh_tokens, connected_accounts. db:push (the obvious fallback) was also broken: drizzle-kit's esbuild loader can't resolve the explicit ./x.js schema imports.

Per Craig's direction (Option 1): fresh DB assumed, no reconciliation — one clean baseline, schema as the single source of truth.

Fix

  • Resolve drizzle-kit imports: point it at compiled dist/schema/*.js; wire tsc && into db:push/db:generate/db:studio.
  • One complete baseline: replaced the partial hand-written migrations with generated 0000_complete_baseline.sql covering all 136 tables. Prepended pgvector CREATE EXTENSION (drizzle can't model extensions) and moved the HNSW + unique (email_id, model) embedding indexes into the schema so they're emitted and won't be dropped by push.
  • Seed login fix: the hardcoded bcrypt hash never matched password123, so the seeded admin could never log in — replaced with a real argon2id hash.
  • Drift guard: new scripts/check-schema-drift.sh + db:check-drift + a CI Schema/Migration Drift job that fails when the schema has changes not captured by a migration. Prevents the "17-of-137" trap from recurring.

Verification (real Postgres 16, fresh DB, vector ext created as superuser)

  • db:migrate136 tables
  • db:seed → succeeds, prints tenant API key ✅
  • API boots (/health 200) ✅
  • Real login (POST /v1/auth/login, email+password → RS256 JWT) ✅
  • Inbox (GET /v1/messages with that token) → 200; rejects without token → 401 ✅
  • Drift guard tested both ways: in-sync passes; injected column fails with the would-be diff ✅
  • bun run typecheck clean; db/api build clean (the only build/test failures in-sandbox are @alecrae/admin Google-Fonts fetch — a network limit, green in CI)

Deploy notes

  • Run CREATE EXTENSION vector as a Postgres superuser once before db:migrate (the non-superuser alecrae role can't create it; IF NOT EXISTS then makes the in-migration call a no-op).
  • Production also requires WEBAUTHN_RP_ID + WEBAUTHN_ORIGIN env — the API throws on boot without them.

Supersedes the incremental 0012 fix from the first push (folded into the baseline).

https://claude.ai/code/session_01McgJLnTbZG9mwhfwd5iAX3

ccantynzcommented Jun 11, 2026

Originally written by @vercel[bot] on GitHub.


Deployment failed with the following error:

There is no GitHub account connected to this Vercel account.
ccantynzcommented Jun 11, 2026

Originally written by @vercel[bot] on GitHub.


The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
alec-rae-com-admin Ready Ready Preview, Comment Jun 11, 2026 6:34am

Cross-repo impact

See what breaks downstream if this PR merges.

Analyze →
⮌ Merged

This pull request was merged into main.

c comment · e edit title · m merge · a approve · r request changes · ? shortcuts