Skip to content

release: merge develop → main (persist user state fix)#3

Merged
atomize merged 2 commits into
mainfrom
develop
Mar 29, 2026
Merged

release: merge develop → main (persist user state fix)#3
atomize merged 2 commits into
mainfrom
develop

Conversation

@atomize

@atomize atomize commented Mar 29, 2026

Copy link
Copy Markdown
Owner

Release: develop → main

Merges the latest develop into main to trigger a Render redeploy.

Included

  • fix: persist alerts, analyses, and chat history across page refresh (fix: persist alerts, analyses, and chat history across page refresh #2)
    • 3 new SQLite tables: chat_history, alerts_log, analyses_log
    • Eager per-user state push on auth (watchlists, config, budget, reports, chat, alerts, analyses)
    • Write-through persistence for all alerts and analyses
    • Chat messages persisted to DB with conversation context restored on reconnect

Commits

Made with Cursor


Note

Medium Risk
Adds new SQLite persistence and changes WS startup/auth flows to load and broadcast per-user state, which can impact data integrity and runtime behavior on reconnect. Risk is moderate due to schema migrations and additional write paths on every alert/analysis/chat message.

Overview
Persists user state across refresh/reconnect in multi-tenant mode. Alerts and agent analyses are now written through to SQLite (alerts_log, analyses_log) when received/broadcast, and per-user histories are loaded from DB and sent to the client instead of relying only on in-memory recentAlerts/recentAnalyses.

Chat history is now durable. Chat messages (including error responses) are appended to a new chat_history table and conversation context is restored from DB when in-memory history is missing; clearChatHistory also clears the DB.

Auth/reconnect experience is improved. After successful auth, the server eagerly pushes per-user state (alert/analysis/chat history plus config/watchlist/schedule/budget/reports) via a new sendPerUserState path.

Written by Cursor Bugbot for commit 75a5730. This will update automatically on new commits. Configure here.

atomize added 2 commits March 29, 2026 13:31
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
fix: persist alerts, analyses, and chat history across page refresh
@atomize
atomize merged commit 8ff4314 into main Mar 29, 2026
2 checks passed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

content: r.content,
timestamp: r.created_at,
}))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported getChatHistoryForUser is never called anywhere

Low Severity

getChatHistoryForUser (renamed from the previous getChatHistory) is exported but never imported or called anywhere in the codebase. The broadcaster.ts imports getChatHistory directly from db.ts instead and does its own formatting in sendPerUserState. This function also hydrates the in-memory conversations cache, which suggests it was intended to replace the direct DB call in the broadcaster — but the integration was missed, leaving this as dead code.

Fix in Cursor Fix in Web

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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported appendAlertLog and appendAnalysisLog never called

Low Severity

appendAlertLog and appendAnalysisLog (per-user variants) are exported but never imported or called anywhere. All call sites use the ForAllUsers variants (appendAlertLogForAllUsers, appendAnalysisLogForAllUsers) instead. These are dead exports that add confusion about which function to use.

Additional Locations (1)
Fix in Cursor Fix in Web

} catch (err) {
log.warn(`Failed to persist analysis: ${(err as Error).message}`)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alert/analysis persistence logic duplicated across four locations

Medium Severity

The same try/catch pattern for persisting alerts and analyses (check isMultiTenant(), extract ID with fallback, call appendAlertLogForAllUsers/appendAnalysisLogForAllUsers, catch and warn) is copy-pasted in four separate locations in broadcaster.ts: broadcastToAll, the onAlert callback, and the WS agent_analysis and alert route handlers. Any future change to the persistence logic (e.g., deduplication, retention) would require updating all four spots.

Additional Locations (2)
Fix in Cursor Fix in Web

handleRequestReports(ws, undefined)
} catch (err) {
log.warn(`Failed to send per-user state for ${auth.email}: ${(err as Error).message}`)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broad try-catch causes total state loss on single failure

Medium Severity

The sendPerUserState function wraps all state-sending operations (alerts, analyses, chat, agent config, watchlists, schedule config, budget, reports) in a single try-catch. A JSON.parse failure on one corrupt alert or analysis payload will abort the entire function, silently preventing the user from receiving their chat history, watchlists, config, and reports on reconnect. This undermines the core goal of the PR — persisting state across page refresh — because one bad row in alerts_log can block all other state delivery.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant