Problem
The committed migration set cannot reproduce the schema the app actually runs on. The dev sqlite contains a table and two users columns that no checked-in migration (or framework setup action) creates, so any database provisioned from database/migrations/** — i.e. the production launch DB — 500s on three core flows: following a judge, claiming a judge profile, and submitting a review (the email-verification gate). On top of that, 59 dead .ts migrations sit in the same directory describing a different schema, and indexes named _unique don't actually enforce uniqueness. This is a launch blocker.
Findings
No migration creates
judge_followsorusers.claimed_judge_id/users.email_verified_at. Grep acrossdatabase/migrations/has zero hits for all three;database/migrations/0000000035-create-users-table.sqllacks both columns, and there is no JudgeFollow model. Consumers hard-depend on them:app/Actions/Judges/FollowJudgeAction.ts:47,app/Actions/Me/ClaimJudgeProfileAction.ts:52,64,app/Actions/Reviews/SubmitReviewAction.ts:116. Framework auth writesusers.email_verified_at(storage/framework/core/auth/src/email-verification.ts:198) but its setup never ALTERsusers(storage/framework/core/actions/src/auth/setup.ts:218-251) — that gap is also filed upstream, but bench needs its own.sqlregardless. Dev only works because bun-query-builder deletesalter-*migrations after executing them, leaving no committed artifact.59 dead
.tsmigrations ship divergent schema. The runner executes only*.sql(storage/framework/core/database/src/migrations.ts:152,474,845), so the 59 generated.tsfiles never run — including1749459728601-create-users-table.ts:9declaringemailunique (the live DB has no unique email index) and a whole commerce suite (orders/carts/gift_cards) with no.sqlcounterpart. Anyone reading them as canonical gets the wrong schema.Indexes named
_uniquethat don't dedupe.0000000089-create-review_drafts_user_unique-...sqlis a plainCREATE INDEX, andapp/Actions/Me/DraftAction.ts:85-110is a non-atomic check-then-insert — two concurrent autosave tabs create duplicate drafts.app/Actions/Reviews/FlagReviewAction.ts:21-22,66-68documents a partial-unique flag-dedup index onreview_flags(judge_review_id, user_id)that exists nowhere. Root cause:app/Models/ReviewDraft.ts:24omitsunique: true, though the generator supports unique and partial indexes (storage/framework/core/database/src/drivers/sqlite.ts:519-529).
Acceptance criteria
- New
.sqlmigrations addusers.email_verified_atandusers.claimed_judge_id, and createjudge_followswith the UNIQUEjudge_follows_user_judge_uniqueindex plus both lookup indexes, mirroring the live DDL - Running all
database/migrations/*.sqlagainst a scratch sqlite file reproduces the dev DB'ssqlite_master(tables/columns/indexes) for app tables - Follow/unfollow, judge-claim, and review-submit all succeed against the freshly migrated scratch DB
- The 59 dead
.tsfiles are deleted fromdatabase/migrations/(git keeps history) review_drafts_user_uniqueis recreated as a real UNIQUE index onreview_drafts(user_id)(dedupe existing rows first), withReviewDraftmodel marking the column unique so regeneration matches- A partial UNIQUE index exists on
review_flags(judge_review_id, user_id) WHERE user_id IS NOT NULL, making FlagReviewAction's comments true; both actions keep their soft UNIQUE-violation handling as the UX layer
From the 2026-06-10 production-readiness audit.
Upstream (framework root causes):
- https://github.com/stacksjs/stacks/issues/1948 — auth setup never creates users.email_verified_at
- https://github.com/stacksjs/stacks/issues/1952 — migrate runner skips .ts/unique-index migrations