Skip to content

fix: chat console — message isolation, timestamps, day groups, delete, and RCA cards#40

Merged
WZ merged 9 commits into
mainfrom
feature/console-chat-fix
Mar 25, 2026
Merged

fix: chat console — message isolation, timestamps, day groups, delete, and RCA cards#40
WZ merged 9 commits into
mainfrom
feature/console-chat-fix

Conversation

@WZ
Copy link
Copy Markdown
Owner

@WZ WZ commented Mar 25, 2026

Summary

  • Fix investigation message leak — Deep Investigation follow-up Q&A no longer appears in main console
  • Preserve RCA completion cards — investigation summaries still render as clickable RCA cards with severity/confidence
  • Add timestamps to every chat message (local time display)
  • Add day group separators (Today / Yesterday / date) for temporal structure
  • Add delete single message + Clear All functionality with safety guards
  • Add unread marker (red "NEW" line based on window focus)
  • Add empty state and loading state
  • Extend WebSocket chat:stream_end with id, createdAt, investigationId for live message management
  • Persist investigation acknowledgment ("Starting investigation of X...") to console
  • Fix listMessages() to return latest-N instead of oldest-N
  • Add rowid tiebreaker for deterministic ordering within same-second timestamps

Key Design Decision

The investigation_id filter distinguishes between:

  • Follow-up Q&A (filtered OUT of console) — messages stored by Deep Investigation panel
  • RCA completion summaries (KEPT in console) — identified by content LIKE '**Root Cause:**%', enriched with report data, rendered as clickable RCA cards

Test Coverage

  • Backend: 100% coverage (9/9 paths tested)
  • 54 test files, 657 tests passing (+8 new)
  • All SQL parameterized, DELETE operations guarded

Test plan

  • All Vitest tests pass (657 tests, 54 files)
  • Type check passes (npx tsc --noEmit)
  • Investigation messages no longer appear in main console
  • RCA cards render correctly after investigation completion
  • Deep Investigation panel still loads its own messages
  • Delete single message returns 404 for investigation messages
  • Clear All preserves investigation messages
  • Day separators and timestamps display correctly
  • Empty state renders when no messages
  • Tested end-to-end: trigger investigation → see RCA card in console

🤖 Generated with Claude Code

Wilson Li added 9 commits March 25, 2026 08:52
…ete methods

- listMessages(): when no investigationId, filter with WHERE investigation_id
  IS NULL and use latest-N subquery (DESC limit then ASC) instead of oldest-N
- listRecentMessages(): add WHERE investigation_id IS NULL to exclude
  investigation follow-up messages from console context
- Add deleteMessage(id): deletes a console message by id (guards against
  deleting investigation messages)
- Add clearConsoleMessages(): bulk-deletes all console messages, returns count
Add DELETE /api/messages/:id (single console message) and DELETE
/api/messages (clear all console messages) routes. Extend the
chat:stream_end WebSocket type with id, createdAt, and investigationId
fields so the client can display timestamps and target messages for
deletion. Wire up the new fields in ws-handler for both conversational
chat and deep-investigate flows.
… states

- Extend ChatMessage with id/createdAt; populate from API and stream_end events
- Filter out investigation-scoped chat/stream_end WS events from console feed
- Show per-message timestamps (JetBrains Mono 10px, local time)
- Insert day group separators (Today/Yesterday/Mar 22) between date boundaries
- Add delete button (Trash2 icon) on hover for user messages with pessimistic delete
- Add Clear All button in header with confirmation dialog
- Show empty state with MessageSquare icon when no messages
- Show spinner during initial history fetch (historyLoading state)
- Force scroll to bottom after initial history load (initialScrollDone ref)
- Add unread marker based on consoleFeed:lastVisitedAt localStorage timestamp
- Remove dead investigation-enrichment code from history fetch
- Fix existing test: listMessages(50) now returns 1 (console only), not 2
- Add 7 new tests: investigation message exclusion, scoped queries,
  latest-N ordering, deleteMessage guards, clearConsoleMessages isolation
- Fix listMessages/listRecentMessages queries to use rowid as tiebreaker
  for deterministic ordering within same-second timestamps
The investigationId filter on chat:stream_end was unconditionally
skipping all investigation messages, including when in deep investigation
mode. This caused deep investigation assistant replies to be silently
dropped. Fixed by only filtering when NOT in deep mode.
The "Starting investigation of X..." message was only sent as a live
WebSocket event but never persisted to the DB. After the investigation_id
filter fix, the investigation summary (which WAS persisted) is correctly
filtered from the console, leaving no visible response. Now the
acknowledgment is saved without investigationId so it appears in the
console as a permanent record of the investigation command.
The console showed "Starting investigation..." permanently with no
completion status. Now persists a completion notice WITHOUT
investigationId: "Investigation of X completed — 90% confidence.
[View results →]". The link navigates to the investigation view.
Also persists failure notices for failed investigations.
- Completion notice uses viewInvestigationId (not investigationId) so
  it passes the console filter but still links to the investigation
- ChatPane chat handler now reads id/createdAt/viewInvestigationId from
  chat events for live rendering with timestamps
- Failed investigations also persist a console-visible notice
…ries

The investigation_id filter was too aggressive — it removed RCA
completion summaries (which rendered as clickable cards) along with
the Deep Investigation follow-up Q&A (the actual leak).

Fix: DB query now includes messages where content starts with
"**Root Cause:**" (completion summaries) even if they have an
investigation_id. ChatPane WS handler allows chat events with
report data through. Enrichment code restored to fetch investigation
reports for RCA card rendering on history load.

This preserves the original user experience: investigation commands
show an acknowledgment, then an RCA card with root cause, confidence,
severity — clickable to view the full investigation.
@WZ WZ force-pushed the feature/console-chat-fix branch from b7cfd10 to f81aff5 Compare March 25, 2026 18:04
@WZ WZ changed the title fix: chat console history — message isolation, timestamps, day groups, and management fix: chat console — message isolation, timestamps, day groups, delete, and RCA cards Mar 25, 2026
@WZ WZ merged commit 5966aa0 into main Mar 25, 2026
1 check passed
WZ added a commit that referenced this pull request Apr 2, 2026
…, and RCA cards (#40)

* fix(db): filter investigation messages from console queries + add delete methods

- listMessages(): when no investigationId, filter with WHERE investigation_id
  IS NULL and use latest-N subquery (DESC limit then ASC) instead of oldest-N
- listRecentMessages(): add WHERE investigation_id IS NULL to exclude
  investigation follow-up messages from console context
- Add deleteMessage(id): deletes a console message by id (guards against
  deleting investigation messages)
- Add clearConsoleMessages(): bulk-deletes all console messages, returns count

* feat(server): DELETE message routes + stream_end metadata fields

Add DELETE /api/messages/:id (single console message) and DELETE
/api/messages (clear all console messages) routes. Extend the
chat:stream_end WebSocket type with id, createdAt, and investigationId
fields so the client can display timestamps and target messages for
deletion. Wire up the new fields in ws-handler for both conversational
chat and deep-investigate flows.

* feat(web): timestamps, day groups, delete, unread marker, and loading states

- Extend ChatMessage with id/createdAt; populate from API and stream_end events
- Filter out investigation-scoped chat/stream_end WS events from console feed
- Show per-message timestamps (JetBrains Mono 10px, local time)
- Insert day group separators (Today/Yesterday/Mar 22) between date boundaries
- Add delete button (Trash2 icon) on hover for user messages with pessimistic delete
- Add Clear All button in header with confirmation dialog
- Show empty state with MessageSquare icon when no messages
- Show spinner during initial history fetch (historyLoading state)
- Force scroll to bottom after initial history load (initialScrollDone ref)
- Add unread marker based on consoleFeed:lastVisitedAt localStorage timestamp
- Remove dead investigation-enrichment code from history fetch

* test(db): add message isolation tests + fix rowid tiebreaker in queries

- Fix existing test: listMessages(50) now returns 1 (console only), not 2
- Add 7 new tests: investigation message exclusion, scoped queries,
  latest-N ordering, deleteMessage guards, clearConsoleMessages isolation
- Fix listMessages/listRecentMessages queries to use rowid as tiebreaker
  for deterministic ordering within same-second timestamps

* fix(web): preserve deep investigation replies in stream_end filter

The investigationId filter on chat:stream_end was unconditionally
skipping all investigation messages, including when in deep investigation
mode. This caused deep investigation assistant replies to be silently
dropped. Fixed by only filtering when NOT in deep mode.

* fix(server): persist investigation acknowledgment to messages table

The "Starting investigation of X..." message was only sent as a live
WebSocket event but never persisted to the DB. After the investigation_id
filter fix, the investigation summary (which WAS persisted) is correctly
filtered from the console, leaving no visible response. Now the
acknowledgment is saved without investigationId so it appears in the
console as a permanent record of the investigation command.

* fix(server+web): show investigation completion in console with link

The console showed "Starting investigation..." permanently with no
completion status. Now persists a completion notice WITHOUT
investigationId: "Investigation of X completed — 90% confidence.
[View results →]". The link navigates to the investigation view.
Also persists failure notices for failed investigations.

* fix(server+web): investigation completion notice + viewInvestigationId

- Completion notice uses viewInvestigationId (not investigationId) so
  it passes the console filter but still links to the investigation
- ChatPane chat handler now reads id/createdAt/viewInvestigationId from
  chat events for live rendering with timestamps
- Failed investigations also persist a console-visible notice

* fix: restore RCA cards in console — filter follow-ups only, not summaries

The investigation_id filter was too aggressive — it removed RCA
completion summaries (which rendered as clickable cards) along with
the Deep Investigation follow-up Q&A (the actual leak).

Fix: DB query now includes messages where content starts with
"**Root Cause:**" (completion summaries) even if they have an
investigation_id. ChatPane WS handler allows chat events with
report data through. Enrichment code restored to fetch investigation
reports for RCA card rendering on history load.

This preserves the original user experience: investigation commands
show an acknowledgment, then an RCA card with root cause, confidence,
severity — clickable to view the full investigation.

---------

Co-authored-by: Wilson Li <wli02@fortinet.com>
@WZ WZ deleted the feature/console-chat-fix branch April 15, 2026 17:40
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