v0.29.2 - Hotfix: missing slug column migration on prod
- 2026-06-08
Bugs
- [x] All
/api/campaignsand/api/characterscalls failed in production with 500Failed to fetch campaignsimmediately after v0.29.0 deploy. Root cause:migrations/0060_add_slugs.sqlshipped with v0.29.0 but was inert — the platform doesn't run drizzle journal migrations in prod, it uses an in-process idempotentALTER TABLE … ADD COLUMN IF NOT EXISTS …runner inpackages/server-core/src/db.ts(postgresApplyColumnMigrations/sqliteApplyColumnMigrations), and that runner had no entries forcampaigns.slug/maps.slug/characters.slug, so prod's table never got the column. Drizzle then raisedcolumn "slug" does not existon every campaign query. (Fixed: added the threeALTER TABLE … ADD COLUMN IF NOT EXISTS slug TEXTstatements + the threeCREATE UNIQUE INDEX IF NOT EXISTS …indexes to both runtime paths indb.ts. Same pattern as the v0.27.3 hotfix for map timestamps. Ran the SQL by hand on prod RDS to restore service; this commit prevents the next cold-start / fresh deploy from re-introducing the gap. New regression testdbSchema.slugs.test.ts(3 tests) pins the runtime ALTERs to the migration file.) - [x] A mysterious dropdown was fixed to the lower-left of the map showing the impersonated token's name and not responding to clicks (right-click on the map was hijacked, and DevTools' element-picker selected the canvas underneath). Root cause: a
<Select>insideCurrentTokenBarContainer(apps/web/src/components/UI.tsx) — the "player character selector" — was wrapped in aBoxwithpointerEvents: 'none', so it rendered visibly but couldn't be interacted with. The "switch impersonated token" UI is already covered by Character Context (CharacterContextBreadcrumb), so the broken mini-selector was redundant. (Fixed: removed theplayerCharacterSelectordefinition, its mobile fallthrough render block, and its desktop fragment render. Updated source-pin testsUI.hideImpersonatedBar.test.tsandUI.mobileImpersonate.test.tsto assert the new contract — the impersonated-token selector is no longer present anywhere, and selectedToken bars render in the shared mobile/desktop fragment.)