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 <noreply@anthropic.com>
This commit is contained in:
Nuwan 2026-02-08 20:44:01 +05:30
parent d162e6cd89
commit 76805f855c
2 changed files with 178 additions and 4 deletions

View File

@ -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 | - |

View File

@ -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"
---
<objective>
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.
</objective>
<execution_context>
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
</context>
<tasks>
<task type="auto">
<name>Task 1: Add MAX_MESSAGES limit to sessionChatSlice.js</name>
<files>jam-ui/src/store/features/sessionChatSlice.js</files>
<action>
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).
</action>
<verify>
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`
</verify>
<done>
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.
</done>
</task>
<task type="auto">
<name>Task 2: Add clearAllMessages action and dispatch on session leave</name>
<files>jam-ui/src/store/features/sessionChatSlice.js, jam-ui/src/components/client/JKSessionScreen.js</files>
<action>
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)
</action>
<verify>
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`
</verify>
<done>
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.
</done>
</task>
</tasks>
<verification>
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)
</verification>
<success_criteria>
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
</success_criteria>
<output>
After completion, create `.planning/phases/21-chat-window-fixes/21-01-SUMMARY.md`
</output>