-- ========================================== -- Migration 006: Unify Chats → Channels -- ========================================== -- "Everything is a channel." Merges the separate chats/channels -- schema into a single unified channel model. -- -- The old channels/channel_members/channel_messages tables (from 001) -- were never populated — safe to drop and rebuild on top of chats. -- ========================================== -- ── 1. Drop unused legacy channel tables ──── -- These were placeholders from 001; no data, no handlers. -- Order matters: drop dependents first. -- Remove FK references in tool_usage_log before dropping ALTER TABLE tool_usage_log DROP CONSTRAINT IF EXISTS tool_usage_log_channel_id_fkey; ALTER TABLE tool_usage_log DROP COLUMN IF EXISTS channel_id; DROP TABLE IF EXISTS channel_messages CASCADE; DROP TABLE IF EXISTS channel_members CASCADE; DROP TABLE IF EXISTS channels CASCADE; -- Drop the old trigger (will re-create after rename) DROP TRIGGER IF EXISTS channels_updated_at ON channels; -- ── 2. Rename chats → channels ────────────── ALTER TABLE chats RENAME TO channels; ALTER TABLE chat_messages RENAME TO messages; -- Rename columns to match new schema ALTER TABLE messages RENAME COLUMN chat_id TO channel_id; -- Rename indexes ALTER INDEX IF EXISTS idx_chats_user RENAME TO idx_channels_user; ALTER INDEX IF EXISTS idx_chats_updated RENAME TO idx_channels_updated; ALTER INDEX IF EXISTS idx_chats_tags RENAME TO idx_channels_tags; ALTER INDEX IF EXISTS idx_chat_messages_chat RENAME TO idx_messages_channel; -- Rename constraints (PK and FK auto-renamed with table on some PG versions, -- but let's be explicit for the FK) -- Note: PG auto-renames PKs but not FKs or check constraints -- Rename the updated_at trigger DROP TRIGGER IF EXISTS chats_updated_at ON channels; CREATE TRIGGER channels_updated_at BEFORE UPDATE ON channels FOR EACH ROW EXECUTE FUNCTION update_updated_at(); -- ── 3. Add channel type + description ─────── ALTER TABLE channels ADD COLUMN IF NOT EXISTS type VARCHAR(20) DEFAULT 'direct'; ALTER TABLE channels ADD COLUMN IF NOT EXISTS description TEXT; COMMENT ON COLUMN channels.type IS 'direct=1:1 AI chat, group=multi-model, channel=named persistent'; -- Backfill: all existing rows are 1:1 AI chats UPDATE channels SET type = 'direct' WHERE type IS NULL; -- Index on type for filtered queries CREATE INDEX IF NOT EXISTS idx_channels_type ON channels(type); -- ── 4. Add message tree (parent_id) ───────── ALTER TABLE messages ADD COLUMN IF NOT EXISTS parent_id UUID REFERENCES messages(id) ON DELETE SET NULL; CREATE INDEX IF NOT EXISTS idx_messages_parent ON messages(parent_id); -- Backfill linear parent chains on existing messages. -- Each message's parent is the previous message in the same channel (by created_at). WITH ordered AS ( SELECT id, channel_id, created_at, LAG(id) OVER (PARTITION BY channel_id ORDER BY created_at) AS prev_id FROM messages ) UPDATE messages m SET parent_id = o.prev_id FROM ordered o WHERE m.id = o.id AND o.prev_id IS NOT NULL AND m.parent_id IS NULL; -- ── 5. Add participant columns on messages ── -- Decouples messages from user-only: AI models are participants too. ALTER TABLE messages ADD COLUMN IF NOT EXISTS participant_type VARCHAR(10) DEFAULT 'user'; ALTER TABLE messages ADD COLUMN IF NOT EXISTS participant_id VARCHAR(255); COMMENT ON COLUMN messages.participant_type IS 'user or model'; COMMENT ON COLUMN messages.participant_id IS 'user UUID or model identifier string'; -- Backfill: user messages get the channel owner's user_id; -- assistant messages get the model name as participant_id. UPDATE messages m SET participant_type = CASE WHEN m.role = 'assistant' THEN 'model' ELSE 'user' END, participant_id = CASE WHEN m.role = 'assistant' THEN COALESCE(m.model, 'unknown') ELSE (SELECT c.user_id::text FROM channels c WHERE c.id = m.channel_id) END WHERE m.participant_id IS NULL; -- ── 6. Update model_routing_log FK ────────── -- The FK column was named chat_id — rename it ALTER TABLE model_routing_log RENAME COLUMN chat_id TO channel_id; ALTER INDEX IF EXISTS idx_routing_log_chat RENAME TO idx_routing_log_channel; -- message_id FK still valid (messages table was renamed, FK follows) -- ── 7. Update tool_usage_log ──────────────── -- We already dropped the old channel_id column above. -- Re-add it pointing to the unified channels table. -- Also rename the old chat_id column. ALTER TABLE tool_usage_log RENAME COLUMN chat_id TO channel_id; -- The FK auto-follows the table rename, but let's be safe: -- (chat_id FK pointed to chats(id), which is now channels(id) — PG handles this)