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

Story 145 implemented localStorage persistence for chat history across page reloads. This is no longer working — refreshing the page loses all conversation context. This is a regression of the feature delivered in story 145.

## How to Reproduce

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. 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. 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 (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 (2026-03-17)

### 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<Message>` 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.
