Fix broken drizzle migration chain and add prevention guidelines
The migration system was broken due to three issues: 1. Snapshot 0067 had an invalid `autoincrement` field (PostgreSQL doesn't support this) causing Zod validation to fail with "data is malformed" 2. Migrations 0068 and 0069 were missing snapshot files, breaking the snapshot chain required by `drizzle-kit generate` 3. Orphaned file 0048_mean_enchantress.sql existed outside the journal Fixed by removing the invalid field, regenerating migrations 0068-0069 with proper snapshots via `drizzle-kit generate`, deleting the orphaned file, and adding a no-op verification migration (0070). Made migration 0069 idempotent with IF EXISTS/IF NOT EXISTS guards for production safety. Updated CLAUDE.md with rules to prevent manual migration/journal editing and ensure snapshots are always generated. https://claude.ai/code/session_01JuVHpRPa974MKSHoXNGkgU
This commit is contained in:
parent
a395f10064
commit
651f7d1141
10 changed files with 13446 additions and 41 deletions
17
CLAUDE.md
17
CLAUDE.md
|
|
@ -205,9 +205,20 @@ Do not consider a feature complete until tests are written and passing.
|
||||||
|
|
||||||
1. Update `database/schema.ts` with new tables/columns
|
1. Update `database/schema.ts` with new tables/columns
|
||||||
2. Run `npm run db:generate` to create migration
|
2. Run `npm run db:generate` to create migration
|
||||||
3. Run `npm run db:migrate` to apply migration
|
3. **Verify** the generated migration SQL in `drizzle/` is correct
|
||||||
4. Add model functions in `app/models/` for new queries
|
4. **Verify** a corresponding snapshot was created in `drizzle/meta/` (e.g., `0070_snapshot.json`)
|
||||||
5. Update TypeScript types if needed
|
5. Run `npm run db:migrate` to apply migration
|
||||||
|
6. Add model functions in `app/models/` for new queries
|
||||||
|
7. Update TypeScript types if needed
|
||||||
|
|
||||||
|
**Drizzle Migration Rules (CRITICAL):**
|
||||||
|
- **NEVER** manually create migration SQL files or journal entries. Always use `drizzle-kit generate` or `drizzle-kit generate --custom` so that snapshot files are created automatically.
|
||||||
|
- **NEVER** edit `drizzle/meta/_journal.json` by hand. The journal and snapshots must stay in sync — drizzle-kit manages both.
|
||||||
|
- Every migration **must** have a matching snapshot in `drizzle/meta/`. Missing snapshots cause `drizzle-kit generate` to fail with "data is malformed" errors, which blocks all future migration generation.
|
||||||
|
- Snapshot JSON must only contain PostgreSQL-valid fields. Do not add `"autoincrement"` to column definitions — PostgreSQL uses sequences/serial instead, and invalid fields cause Zod validation failures.
|
||||||
|
- If `drizzle-kit generate` prompts for column renames interactively, use `drizzle-kit generate --custom` instead and write the migration SQL by hand (include data backfill logic as needed).
|
||||||
|
- After generating, run `drizzle-kit generate` again to confirm "No schema changes" — this validates the snapshot chain is healthy.
|
||||||
|
- For destructive migrations (dropping columns, renaming), make the SQL idempotent with `IF EXISTS`/`IF NOT EXISTS` guards where possible.
|
||||||
|
|
||||||
### Adding Socket.IO Events
|
### Adding Socket.IO Events
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
DO $$ BEGIN
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'ncaam_bracket' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'simulator_type')) THEN
|
|
||||||
ALTER TYPE "public"."simulator_type" ADD VALUE 'ncaam_bracket';
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'ncaaw_bracket' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'simulator_type')) THEN
|
|
||||||
ALTER TYPE "public"."simulator_type" ADD VALUE 'ncaaw_bracket';
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
DO $$ BEGIN
|
|
||||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'nba_bracket' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'simulator_type')) THEN
|
|
||||||
ALTER TYPE "public"."simulator_type" ADD VALUE 'nba_bracket';
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
--> statement-breakpoint
|
|
||||||
ALTER TABLE "leagues" ADD COLUMN IF NOT EXISTS "discord_webhook_url" varchar(500);
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
-- Step 1: Add new columns as nullable
|
-- Step 1: Add new columns as nullable (IF NOT EXISTS for idempotency)
|
||||||
ALTER TABLE "sports_seasons" ADD COLUMN "draft_on" date;--> statement-breakpoint
|
ALTER TABLE "sports_seasons" ADD COLUMN IF NOT EXISTS "draft_on" date;--> statement-breakpoint
|
||||||
ALTER TABLE "sports_seasons" ADD COLUMN "draft_off" date;--> statement-breakpoint
|
ALTER TABLE "sports_seasons" ADD COLUMN IF NOT EXISTS "draft_off" date;--> statement-breakpoint
|
||||||
|
|
||||||
-- Step 2: Backfill data based on existing is_draftable flag
|
-- Step 2: Backfill data based on existing is_draftable flag (only if column still exists)
|
||||||
UPDATE "sports_seasons"
|
DO $$ BEGIN
|
||||||
SET "draft_on" = '2026-04-06', "draft_off" = '2099-12-31'
|
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'sports_seasons' AND column_name = 'is_draftable') THEN
|
||||||
WHERE "is_draftable" = true;--> statement-breakpoint
|
UPDATE "sports_seasons" SET "draft_on" = '2026-04-06', "draft_off" = '2099-12-31' WHERE "is_draftable" = true;
|
||||||
|
UPDATE "sports_seasons" SET "draft_on" = '2020-01-01', "draft_off" = '2020-12-31' WHERE "is_draftable" = false;
|
||||||
UPDATE "sports_seasons"
|
END IF;
|
||||||
SET "draft_on" = '2020-01-01', "draft_off" = '2020-12-31'
|
END $$;--> statement-breakpoint
|
||||||
WHERE "is_draftable" = false;--> statement-breakpoint
|
|
||||||
|
|
||||||
-- Step 3: Apply NOT NULL constraints
|
-- Step 3: Apply NOT NULL constraints
|
||||||
ALTER TABLE "sports_seasons" ALTER COLUMN "draft_on" SET NOT NULL;--> statement-breakpoint
|
ALTER TABLE "sports_seasons" ALTER COLUMN "draft_on" SET NOT NULL;--> statement-breakpoint
|
||||||
ALTER TABLE "sports_seasons" ALTER COLUMN "draft_off" SET NOT NULL;--> statement-breakpoint
|
ALTER TABLE "sports_seasons" ALTER COLUMN "draft_off" SET NOT NULL;--> statement-breakpoint
|
||||||
|
|
||||||
-- Step 4: Drop old column
|
-- Step 4: Drop old column (IF EXISTS for idempotency)
|
||||||
ALTER TABLE "sports_seasons" DROP COLUMN "is_draftable";
|
ALTER TABLE "sports_seasons" DROP COLUMN IF EXISTS "is_draftable";
|
||||||
|
|
|
||||||
3
drizzle/0070_verify_migrations.sql
Normal file
3
drizzle/0070_verify_migrations.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- Verification migration: confirms drizzle migration chain is healthy.
|
||||||
|
-- This is a no-op migration that validates the migration system works end-to-end.
|
||||||
|
SELECT 1;
|
||||||
|
|
@ -1159,8 +1159,7 @@
|
||||||
"name": "world_ranking",
|
"name": "world_ranking",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": false
|
||||||
"autoincrement": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {
|
"indexes": {
|
||||||
|
|
|
||||||
4465
drizzle/meta/0068_snapshot.json
Normal file
4465
drizzle/meta/0068_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
4470
drizzle/meta/0069_snapshot.json
Normal file
4470
drizzle/meta/0069_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
4470
drizzle/meta/0070_snapshot.json
Normal file
4470
drizzle/meta/0070_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -481,16 +481,23 @@
|
||||||
{
|
{
|
||||||
"idx": 68,
|
"idx": 68,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1775000000000,
|
"when": 1775485575205,
|
||||||
"tag": "0068_cs2_major_simulator",
|
"tag": "0068_soft_silk_fever",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idx": 69,
|
"idx": 69,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1744012800000,
|
"when": 1775485599229,
|
||||||
"tag": "0069_draft_window",
|
"tag": "0069_draft_window",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 70,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1775485630297,
|
||||||
|
"tag": "0070_verify_migrations",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue