From 9098c1ba9d53bf46e17c0a8994cb2c106f70193d Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 17 Mar 2026 13:57:28 +0000 Subject: [PATCH] story-kit: done 245_bug_chat_history_persistence_lost_on_page_refresh_story_145_regression --- ...st_on_page_refresh_story_145_regression.md | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/.story_kit/work/5_done/245_bug_chat_history_persistence_lost_on_page_refresh_story_145_regression.md b/.story_kit/work/5_done/245_bug_chat_history_persistence_lost_on_page_refresh_story_145_regression.md index 8ed0987..4919df7 100644 --- a/.story_kit/work/5_done/245_bug_chat_history_persistence_lost_on_page_refresh_story_145_regression.md +++ b/.story_kit/work/5_done/245_bug_chat_history_persistence_lost_on_page_refresh_story_145_regression.md @@ -1,11 +1,14 @@ --- name: "Chat history persistence lost on page refresh (story 145 regression)" +agent: coder-opus --- ## Rejection Notes **2026-03-16:** Previous coder produced zero code changes — feature branch had no diff against master. The coder must actually use `git bisect` to find the breaking commit and produce a surgical fix. Do not submit with no code changes. +**2026-03-17:** Re-opened. Multiple fix attempts have failed. See investigation notes below for the actual root cause. + # Bug 245: Chat history persistence lost on page refresh (story 145 regression) ## Description @@ -16,21 +19,50 @@ Story 145 implemented localStorage persistence for chat history across page relo 1. Open the web UI and have a conversation with the agent 2. Refresh the page (F5 or Cmd+R) +3. Send a new message +4. The LLM has no knowledge of the prior conversation ## Actual Result -Chat history is gone after refresh — the UI shows a blank conversation. +Chat history is gone after refresh — the UI shows a blank conversation. Even if messages appear in the UI (loaded from localStorage), the LLM does not receive them as context on the next exchange. ## Expected Result -Chat history is restored from localStorage on page load, as implemented in story 145. +Chat history is restored from localStorage on page load, as implemented in story 145. The LLM should receive the full conversation history when the user sends a new message after refresh. ## Acceptance Criteria -- [ ] Chat messages survive a full page refresh +- [ ] Chat messages survive a full page refresh (visible in UI) - [ ] Chat messages are restored from localStorage on component mount +- [ ] After refresh, the LLM receives full prior conversation history as context when the user sends the next message - [ ] Behaviour matches the original acceptance criteria from story 145 -## Investigation Notes +## Investigation Notes (2026-03-17) -**Use `git bisect` to find the commit that broke this.** Story 145 delivered working localStorage persistence — something after that regressed it. Find the breaking commit, understand the root cause, and fix it there. Do NOT layer on a new implementation. Revert or surgically fix the regression. +### Root cause analysis + +The frontend correctly: +1. Persists messages to localStorage in `useChatHistory.ts` (key: `storykit-chat-history:{projectPath}`) +2. Loads them on mount +3. Sends the FULL history array to the backend via `wsRef.current?.sendChat(newHistory, config)` in `Chat.tsx` line ~558 + +The backend bug is in `server/src/llm/chat.rs`: +- The `chat()` function receives the full `messages: Vec` from the client +- Line ~283: `let mut current_history = messages.clone()` — correctly clones full history +- Lines ~299-318: Adds 2 system prompts at position 0 and 1 +- Lines ~323-404: Main LLM loop generates new assistant/tool messages +- **Line ~407: `ChatResult { messages: new_messages }` — BUG: returns ONLY the newly generated turn, not the full `current_history`** + +During streaming, the `on_update()` callbacks DO send `current_history[2..]` (full history minus system prompts), which is correct. But there may be a reconciliation issue on the frontend where the final state doesn't include the full history. + +### Key files +- `frontend/src/hooks/useChatHistory.ts` — localStorage persistence +- `frontend/src/components/Chat.tsx` — sends full history, handles `onUpdate` callbacks +- `frontend/src/api/client.ts` — WebSocket client +- `server/src/http/ws.rs` — WebSocket handler, passes messages to chat() +- `server/src/llm/chat.rs` — **THE BUG** at line ~407, ChatResult returns only new_messages + +### What NOT to do +- Do NOT layer on a new localStorage implementation. The localStorage code works fine. +- Do NOT add server-side persistence. The "dumb pipe" architecture is correct. +- The fix should be surgical — ensure the full conversation history round-trips correctly through the backend.