CI surfaced react-hooks/set-state-in-effect on the synchronous setState(computeState(token)) inside the useEffect body. The earlier shape mirrored token -> state via an effect, which is exactly the "you might not need an effect" pattern React 19's eslint rule now flags. Switch to derived state: compute during render, use a useReducer tick to force re-render on the 30s cadence (so relative timestamps stay current even when token props don't change). Same observable behavior, no cascading renders. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5.7 KiB
5.7 KiB
Unified Sessions — Migration Plan
Date: 2026-03-23 Status: Implementation ready Goal: Merge assistant chat into the ai_sessions system so both guided (FlowPilot) and free-form (chat) sessions share the same data model, history, and action system.
Problem
Three separate conversation systems exist:
assistant_chats— free-form chat, JSONB messages inline, no stepsai_sessions+ai_session_steps— guided FlowPilot, separate steps tableai_chat_sessions— flow builder (unrelated, leave alone)
This causes:
- No unified session history
- Assistant chats missing Resolve/Escalate/Update actions
- Dashboard can't show assistant chats in active/recent
- Two separate API surfaces to maintain
Solution
Add session_type to ai_sessions. Chat sessions use conversation_messages JSONB for message history (already exists). Both types share the same status, PSA, escalation, and documentation features.
Data Model Changes
Migration: Add session_type to ai_sessions
ALTER TABLE ai_sessions ADD COLUMN session_type VARCHAR(10) NOT NULL DEFAULT 'guided';
-- Values: 'guided' (FlowPilot), 'chat' (assistant)
How chat sessions use ai_sessions
| ai_sessions column | Chat usage |
|---|---|
session_type |
'chat' |
intake_type |
'free_text' |
intake_content |
{text: "first message"} |
conversation_messages |
Full chat history as JSONB array [{role, content}] |
status |
Same: active/resolved/escalated/paused/abandoned |
problem_summary |
AI-generated from first few messages |
problem_domain |
AI-detected domain |
step_count |
Message count (for display) |
resolution_summary |
Set on resolve |
escalation_reason |
Set on escalate |
| All PSA fields | Same — can link tickets, push notes |
| All timestamps | Same |
What chat sessions DON'T use
ai_session_stepstable — no steps, messages are inconversation_messagesmatched_flow_id/match_score— no flow matchingconfidence_tier/confidence_score— no structured confidencesystem_prompt_snapshot— could store chat system prompt
New field on ai_sessions
title(String 255, nullable) — chat sessions need a title for the sidebar. Guided sessions can useproblem_summary.
Implementation Phases
Phase 1: Backend — Model & Migration
- Alembic migration: add
session_typeVARCHAR(10) default 'guided', addtitleVARCHAR(255) nullable - Update AISession model with new columns
- Update schemas: add
session_typeandtitleto response schemas - Add session_type filter to GET /ai-sessions endpoint
Phase 2: Backend — Chat API on ai_sessions
- Create new endpoints or extend existing:
POST /ai-sessionswithsession_type: 'chat'— creates a chat sessionPOST /ai-sessions/{id}/chat— send message, get AI response (appends to conversation_messages)- Reuse existing: resolve, escalate, pause, abandon, status-update
- Chat AI service: takes conversation_messages, calls Anthropic, appends response
- Auto-generate title from first message (like current assistant chat does)
- Auto-detect problem_domain from conversation
Phase 3: Frontend — Unified Session History
- Update SessionHistoryPage to show both types
- Add type icon: compass/route for guided, message-circle for chat
- Session detail page routes correctly based on type
- Add session_type filter option
Phase 4: Frontend — Assistant Chat on ai_sessions
- Update AssistantChatPage to use ai_sessions API instead of assistant_chats
- Chat sidebar queries ai_sessions with
session_type=chat - Messages read from / write to
conversation_messages - Add header actions: Resolve / Escalate / Share Update / Pause / Close
- Status update modal works the same as FlowPilot
Phase 5: Frontend — Dashboard Integration
- ActiveFlowPilotSessions includes chat sessions (both types)
- RecentFlowPilotSessions includes resolved chats
- Type icon on each card so users see the difference at a glance
Phase 6: Cleanup
- Migrate existing assistant_chat data to ai_sessions (optional — could just start fresh for pilot)
- Deprecate /assistant/* API endpoints
- Remove assistant_chats model (post-pilot)
Visual Differentiators
| Type | Icon | Badge color | Label |
|---|---|---|---|
| Guided (FlowPilot) | <Route size={14} /> |
cyan | "Guided" |
| Chat (Assistant) | <MessageCircle size={14} /> |
purple/violet | "Chat" |
API Surface (after migration)
All under /ai-sessions:
| Endpoint | Both types? | Notes |
|---|---|---|
POST /ai-sessions |
Yes | session_type field determines behavior |
GET /ai-sessions |
Yes | Filter by session_type optional |
GET /ai-sessions/{id} |
Yes | Returns full session with messages or steps |
POST /ai-sessions/{id}/chat |
Chat only | Send/receive messages |
POST /ai-sessions/{id}/respond |
Guided only | Step response |
POST /ai-sessions/{id}/resolve |
Both | Same resolve flow |
POST /ai-sessions/{id}/escalate |
Both | Same escalation |
POST /ai-sessions/{id}/pause |
Both | Same pause |
POST /ai-sessions/{id}/abandon |
Both | Same abandon |
POST /ai-sessions/{id}/status-update |
Both | Same status updates |
Risk Assessment
- Low risk: Adding columns to ai_sessions is additive, no existing data changes
- Medium risk: Frontend routing — need to route to correct page based on session_type
- Data migration: Can skip for pilot — start with fresh chat sessions on new system. Old assistant_chats remain accessible via old API until removed.
- Rollback: session_type column is additive, old assistant_chat endpoints can stay as fallback