feat(backfill): Phase 2 rooms→sessions/chats/chat_messages script
Summary
Phase 2 of the chat→/api/chat/workflow cutover (recoupable/chat#1747): backfill legacy rooms + memories into the new sessions / chats / chat_messages model.
Built in api (not chat) because api owns this data model — so it reuses existing typed helpers and needs no type regeneration. Supersedes the chat-side attempt (recoupable/chat#1750), which had to hand-write helpers and regenerate chat's types because those tables aren't in chat's generated types.
What it does
scripts/backfillRoomsToSessions.ts+scripts/backfill/migrateRoom.ts— orchestration only, no raw supabase (per the repo's lib/supabase rule). For each room: upsert session → chat → messages.- Reuses
insertSession/insertChat/upsertChatMessage, guarded byselectSessions/selectChatsso re-runs are idempotent (deterministicuuidv5session ids;room.idpreserved aschat.idso/chat/[roomId]URLs keep working;upsertChatMessage({ update: false })is write-once). lib/supabase/rooms/selectAllRooms.ts— paginated all-rooms read (existingselectRoomscaps at the PostgREST 1k limit; there are ~23.6k rooms). ReusesselectMemoriesper room.- Adds
uuiddep +backfill:rooms-to-sessionsscript.
Run
SUPABASE_URL=… SUPABASE_KEY=… pnpm backfill:rooms-to-sessions
Idempotent — safe to re-run after a partial failure. Exits non-zero if any room fails.
Test plan
-
pnpm test lib/supabase/rooms/__tests__/selectAllRooms.test.ts— pagination (single page / multi-page / error) green -
tsc --noEmitclean for the backfill files; import chain resolves undertsx - Dry-run against a non-prod project before the real run; reconcile counts (rooms→sessions, memories→chat_messages)
Notes
- The transform assumes
memories.contentis{ role, parts, content }(written byfilterMessageContentForMemories). Pre-partslegacy rows would fail theparts NOT NULLupsert and be reported (non-zero exit), not silently dropped — worth a count of null-partsmemories before the prod run.
🤖 Generated with Claude Code
Summary by cubic
Adds an idempotent backfill to migrate legacy rooms and memories to the new sessions/chats/chat_messages model, with concurrent room processing for faster runs. Supports dry-run, paginated reads, targeted runs (--limit, --room), and safe re-runs; keeps existing /chat/[roomId] URLs working.
-
New Features
- Backfill orchestrator (
scripts/backfillRoomsToSessions.ts,scripts/backfill/migrateRoom.ts) using typed helpers (no raw Supabase). Addsuuid,@types/uuid, and abackfill:rooms-to-sessionsscript. - Dry run (
DRY_RUN=1or--dry-run): reads and checks only; prints a full preview. Targeted runs:--limit=N,--room=<id>. - Pagination: generic
scripts/backfill/paginate.ts+scripts/backfill/selectAllRooms.ts;selectRooms/selectMemoriesgain an optionalrangewith stableidsecondary sort to avoid skips/dupes (existing callers unchanged). - Idempotent re-runs: deterministic session IDs via
uuidv5; preserveroom.idaschat.id; write-once message upserts. - Performance: concurrent room pool (
--concurrency=N, default 20) withPromise.allSettledisolation. upsertChatMessagesdelegates to the single-rowupsertChatMessagehelper (no direct query); duplicates count as success; logs per-row failures and throws if any fail. Tests cover paginate, selectAllRooms, migrateRoom (dry/real/idempotent), andupsertChatMessages.
- Backfill orchestrator (
-
Migration
- Run: SUPABASE_URL=… SUPABASE_KEY=… pnpm
backfill:rooms-to-sessions(DRY_RUN=1/--dry-run,--limit=N,--room=<id>,--concurrency=N) - Safe to re-run; exits non‑zero if any room fails.
- Run: SUPABASE_URL=… SUPABASE_KEY=… pnpm
Summary by CodeRabbit
-
New Features
- Added pagination support for memory and room queries via optional range parameters.
- Added batch upsert functionality for chat messages with fallback handling.
-
Improvements
- Enhanced data retrieval operations with deterministic ordering for consistent pagination results.