fix: persist alerts, analyses, and chat history across page refresh#2
Conversation
In multi-tenant mode, refreshing the dashboard lost all user state because alerts, analyses, and chat were stored only in memory. Added three DB tables (chat_history, alerts_log, analyses_log) and eagerly push per-user state (watchlists, config, budget, reports) on auth so the dashboard is fully populated immediately after reconnect. Made-with: Cursor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| const errorMsg = `Error: ${errMsg}` | ||
| if (isEncryptionEnabled()) { | ||
| try { appendChatMessage(userId, 'assistant', errorMsg) } catch { /* best-effort */ } | ||
| } |
There was a problem hiding this comment.
Error messages persisted to DB but not in-memory
Medium Severity
When the Claude API call fails, the error message is persisted to the DB via appendChatMessage as an 'assistant' role message but is never pushed to the in-memory history array. On reconnect, getChatHistoryDb loads DB rows (including error messages) into the conversation context sent to Claude. This creates a state divergence: before restart, Claude's context has a dangling user message with no assistant response; after restart, Claude sees "Error: ..." as its own prior response. The asymmetry changes Claude's behavior depending on whether a restart occurred.
Additional Locations (1)
| content: r.content, | ||
| timestamp: r.created_at, | ||
| })) | ||
| } |
There was a problem hiding this comment.
Renamed getChatHistoryForUser is exported but never called
Low Severity
getChatHistoryForUser was renamed from getChatHistory to avoid a naming collision with the db.ts export, but no module imports or calls it. The broadcaster.ts calls getChatHistory from db.js directly and formats the results inline. This function — which also pre-populates the in-memory conversations cache — is dead code.
| export function appendAlertLog(userId: number, alertId: string, payload: string): void { | ||
| getDb().prepare( | ||
| "INSERT INTO alerts_log (user_id, alert_id, payload) VALUES (?, ?, ?)" | ||
| ).run(userId, alertId, payload) |
There was a problem hiding this comment.
Unused per-user DB functions never called anywhere
Low Severity
appendAlertLog and appendAnalysisLog are newly added exported functions that are never called anywhere in the codebase. Only their ForAllUsers counterparts are used by broadcaster.ts. These are dead code that adds maintenance burden.


Problem
In multi-tenant mode, refreshing the browser wiped all user-facing state:
Map— gone on refresh, and the Claude API lost all conversation contextuseEffectin each component) — so on refresh the dashboard appeared empty until you clicked aroundChanges
3 new SQLite tables (
db.ts)chat_historyalerts_loganalyses_logAll three auto-migrate via the existing
migrateV2Tables()path.Eager state push on auth (
broadcaster.ts)New
sendPerUserState()runs immediately after successful authentication and pushes:alerts_log)analyses_log)chat_history)This means the dashboard is fully populated the moment the JWT is verified — no more empty panels.
Write-through persistence (
broadcaster.ts)Every alert and analysis is now written to the DB (for all users) at the point it's broadcast, covering:
Chat persistence (
chatHandler.ts)chat_historyon each exchangeclearChatalso truncates the DB tableTest plan
Made with Cursor
Note
Medium Risk
Adds new SQLite persistence and readback paths for user-facing realtime state and eagerly rehydrates state on auth; main risk is schema migration and increased write volume (fan-out inserts to all users) affecting DB performance or storage.
Overview
Persists previously in-memory dashboard state in multi-tenant mode by adding SQLite-backed storage for chat history, alerts, and analyses, and wiring broadcaster/chat flows to write-through these logs.
On successful auth, the WS server now sends a per-user state bundle (recent alerts/analyses, chat history, plus existing agent/watchlist/schedule/budget/reports) so a refreshed dashboard is immediately populated without extra client-side requests.
Written by Cursor Bugbot for commit 6b82518. This will update automatically on new commits. Configure here.