From 76805f855c463c4bfa34855a37141c2b8fac70a8 Mon Sep 17 00:00:00 2001 From: Nuwan Date: Sun, 8 Feb 2026 20:44:01 +0530 Subject: [PATCH] docs(21): create phase plan Phase 21: Chat Window Fixes - 1 plan(s) in 1 wave(s) - 1 parallel, 0 sequential - Ready for execution Co-Authored-By: Claude Opus 4.5 --- .planning/ROADMAP.md | 8 +- .../phases/21-chat-window-fixes/21-01-PLAN.md | 174 ++++++++++++++++++ 2 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 .planning/phases/21-chat-window-fixes/21-01-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 8c093ee11..e9cc7cb51 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -417,11 +417,11 @@ Plans: - [x] 20-01-PLAN.md — Add removeVuState function and integrate with mixer lifecycle - COMPLETE 2026-02-08 #### Phase 21: Chat Window Fixes -**Goal**: Fix identified chat WebSocket listener and state cleanup issues +**Goal**: Fix unbounded message accumulation and add session leave cleanup **Depends on**: Phase 19 **Research**: Unlikely (fixing issues identified in Phase 19) **Requirements**: CHAT-01, CHAT-02, CHAT-03 -**Plans**: TBD (1-2 plans expected) +**Plans**: 1 plan **Success Criteria:** 1. WebSocket listeners are properly removed when chat window closes @@ -431,7 +431,7 @@ Plans: 5. Redux state cleanup happens when session ends or user leaves Plans: -- [ ] 21-01: TBD +- [ ] 21-01-PLAN.md — Bounded message storage and session leave cleanup (CHAT-01, CHAT-03) #### Phase 22: Session Screen Fixes **Goal**: Fix identified session screen useEffect and polling cleanup issues @@ -495,6 +495,6 @@ Phases execute in numeric order: 1 → 2 → ... → 18 → 19 → 20 → 21 → | 18. Integration Tests (Playwright) | v1.3 | 1/1 | Complete | 2026-02-08 | | 19. Audit and Discovery | v1.4 | 1/1 | Complete | 2026-02-08 | | 20. VU Meter Fixes | v1.4 | 1/1 | Complete | 2026-02-08 | -| 21. Chat Window Fixes | v1.4 | 0/TBD | Not started | - | +| 21. Chat Window Fixes | v1.4 | 0/1 | Not started | - | | 22. Session Screen Fixes | v1.4 | 0/TBD | Not started | - | | 23. Verification | v1.4 | 0/TBD | Not started | - | diff --git a/.planning/phases/21-chat-window-fixes/21-01-PLAN.md b/.planning/phases/21-chat-window-fixes/21-01-PLAN.md new file mode 100644 index 000000000..8ad9320e0 --- /dev/null +++ b/.planning/phases/21-chat-window-fixes/21-01-PLAN.md @@ -0,0 +1,174 @@ +--- +phase: 21-chat-window-fixes +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - jam-ui/src/store/features/sessionChatSlice.js + - jam-ui/src/components/client/JKSessionScreen.js +autonomous: true + +must_haves: + truths: + - "Chat messages are limited to MAX_MESSAGES (500) per channel" + - "Oldest messages are removed when new messages exceed the limit" + - "Chat state is cleared when user leaves session" + - "Memory usage does not grow unbounded with chat activity" + artifacts: + - path: "jam-ui/src/store/features/sessionChatSlice.js" + provides: "MAX_MESSAGES constant and clearAllMessages action" + contains: "MAX_MESSAGES" + - path: "jam-ui/src/components/client/JKSessionScreen.js" + provides: "Chat cleanup on session leave" + contains: "clearAllMessages" + key_links: + - from: "sessionChatSlice.js addMessageFromWebSocket" + to: "slice(-MAX_MESSAGES)" + via: "array limit after push" + pattern: "slice.*-MAX_MESSAGES" + - from: "JKSessionScreen.js leave handler" + to: "clearAllMessages action" + via: "dispatch on session leave" + pattern: "dispatch.*clearAllMessages" +--- + + +Implement bounded chat message storage and cleanup to prevent unbounded memory growth. + +Purpose: Fix CHAT-01 (unbounded message growth) and CHAT-03 (no cleanup on session leave) identified in Phase 19 audit. Without these fixes, the messagesByChannel Redux state grows indefinitely during active chat sessions, contributing to memory degradation over time. + +Output: sessionChatSlice.js with MAX_MESSAGES limit enforced on all message additions, and JKSessionScreen.js dispatching clearAllMessages when leaving sessions. + + + +@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md +@/Users/nuwan/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/REQUIREMENTS.md +@.planning/phases/19-audit-and-discovery/19-AUDIT.md + +# Key source files +@jam-ui/src/store/features/sessionChatSlice.js +@jam-ui/src/components/client/JKSessionScreen.js + + + + + + Task 1: Add MAX_MESSAGES limit to sessionChatSlice.js + jam-ui/src/store/features/sessionChatSlice.js + +Add bounded message storage to prevent unbounded memory growth: + +1. Add constant at top of file (after imports, before initialState): + ```javascript + /** + * Maximum messages retained per channel to prevent unbounded memory growth + * Oldest messages are removed when this limit is exceeded + */ + const MAX_MESSAGES = 500; + ``` + +2. In `addMessageFromWebSocket` reducer (around line 202), after pushing and sorting: + - Add slice to limit array: `state.messagesByChannel[channel] = state.messagesByChannel[channel].slice(-MAX_MESSAGES);` + - This keeps only the last MAX_MESSAGES (most recent) + +3. In `fetchChatHistory.fulfilled` reducer (around line 366), after merging and sorting: + - Add same slice: `state.messagesByChannel[channel] = state.messagesByChannel[channel].slice(-MAX_MESSAGES);` + +4. In `uploadAttachment.fulfilled` reducer (around line 517), after pushing and sorting: + - Add same slice: `state.messagesByChannel[channel] = state.messagesByChannel[channel].slice(-MAX_MESSAGES);` + +Why slice(-MAX_MESSAGES): Negative slice keeps last N elements, so oldest messages are dropped first (FIFO queue behavior). + + +1. Grep for MAX_MESSAGES in file: should appear 4 times (1 definition + 3 usages) +2. Grep for "slice(-MAX_MESSAGES)" in file: should appear 3 times +3. Run ESLint: `cd jam-ui && npm run lint -- --quiet src/store/features/sessionChatSlice.js` + + +MAX_MESSAGES constant defined (500), and slice(-MAX_MESSAGES) applied in addMessageFromWebSocket, fetchChatHistory.fulfilled, and uploadAttachment.fulfilled reducers. Message arrays are bounded to 500 per channel. + + + + + Task 2: Add clearAllMessages action and dispatch on session leave + jam-ui/src/store/features/sessionChatSlice.js, jam-ui/src/components/client/JKSessionScreen.js + +Add chat cleanup when leaving sessions: + +**In sessionChatSlice.js:** + +1. Add new reducer `clearAllMessages` in the reducers object (after `clearUploadError`, around line 305): + ```javascript + /** + * Clear all messages when leaving session + * Called from JKSessionScreen when user leaves to prevent stale data + */ + clearAllMessages: (state) => { + state.messagesByChannel = {}; + state.unreadCounts = {}; + state.fetchStatus = {}; + state.fetchError = {}; + state.nextCursors = {}; + // Preserve lastReadAt for persistence across sessions + // Preserve isWindowOpen and windowPosition for UX continuity + }, + ``` + +2. Export the action in the exports (around line 540): + - Add `clearAllMessages` to the destructured exports + +**In JKSessionScreen.js:** + +1. Import clearAllMessages action (around line 58): + - Add `clearAllMessages` to the imports from sessionChatSlice + +2. In handleLeaveWithFeedback callback (around line 916 where clearSession is dispatched): + - Add `dispatch(clearAllMessages());` BEFORE `dispatch(clearSession());` + +3. In the cleanup useEffect (around line 944 where clearSession is dispatched): + - Add `dispatch(clearAllMessages());` BEFORE `dispatch(clearSession());` + +This ensures chat state is cleared both when: +- User explicitly leaves via feedback modal +- Component unmounts (navigation away, session end) + + +1. Grep for clearAllMessages in sessionChatSlice.js: should appear 2 times (reducer + export) +2. Grep for clearAllMessages in JKSessionScreen.js: should appear 3 times (import + 2 dispatches) +3. Run ESLint on both files: + - `cd jam-ui && npm run lint -- --quiet src/store/features/sessionChatSlice.js` + - `cd jam-ui && npm run lint -- --quiet src/components/client/JKSessionScreen.js` + + +clearAllMessages action added to sessionChatSlice.js, exported, and dispatched in both leave paths in JKSessionScreen.js (handleLeaveWithFeedback and unmount cleanup useEffect). Chat state is properly cleaned up when leaving sessions. + + + + + + +1. **Syntax verification:** Both files pass ESLint without errors +2. **Constant verification:** `grep -n "MAX_MESSAGES" sessionChatSlice.js` shows 4 occurrences +3. **Action verification:** `grep -n "clearAllMessages" sessionChatSlice.js JKSessionScreen.js` shows 5 occurrences total +4. **Build verification:** `cd jam-ui && npm run build` completes without errors (if environment supports) + + + +1. MAX_MESSAGES = 500 constant defined in sessionChatSlice.js +2. slice(-MAX_MESSAGES) applied after message push in 3 reducers +3. clearAllMessages action created and exported +4. clearAllMessages dispatched in both leave paths (handleLeaveWithFeedback, unmount cleanup) +5. Both files pass ESLint +6. CHAT-01 and CHAT-03 requirements addressed + + + +After completion, create `.planning/phases/21-chat-window-fixes/21-01-SUMMARY.md` +