# Session Continuity Fix Summary

## Problem
The app was creating new OpenCode sessions for each message instead of reusing existing session, breaking conversation continuity. This affected:
1. **Continue when error** - When a message failed and was retried with a fallback model
2. **Messages in the same chat** - Each subsequent message created a new OpenCode session instead of continuing in the existing one
3. **Builder proceed with build** - Build messages created new sessions
4. **Redo operations** - Redoing a message didn't preserve the original session

## Root Causes

1. **Client-side missing opencodeSessionId**: The frontend (`builder.js`, `app.js`) wasn't sending `opencodeSessionId` in the message payload when creating new messages.

2. **Server-side overwriting session IDs**: The server's `ensureOpencodeSession()` function was creating new OpenCode session IDs even when one already existed, if validation failed or for certain edge cases.

3. **Session detection overwriting**: When a new OpenCode session was auto-created by the CLI, code was overwriting the existing `initialOpencodeSessionId`.

## Fixes Applied

### 1. Client-side (builder.js)
- **sendMessage()**: Added logic to preserve `session.opencodeSessionId` in message payload when sending messages
- **executeBuild()**: Added logic to preserve `session.opencodeSessionId` when starting a build process
- **redoProceedWithBuild()**: Added logic to preserve `session.opencodeSessionId` when redoing a build
- **triggerFallback()**: Enhanced to prioritize message's `opencodeSessionId` over session's (in case message has more recent session)

### 2. Client-side (app.js)
- **sendMessage()**: Added logic to preserve `session.opencodeSessionId` in message payload for regular chat UI

### 3. Server-side (server.js)
- **ensureOpencodeSession()**:
  - When session validation fails, now returns the existing `session.opencodeSessionId` instead of creating a new one
  - This prevents creating new sessions when CLI operations timeout or fail temporarily

- **createOpencodeSession() section**:
  - Only sets `initialOpencodeSessionId` if not already set
  - This prevents overwriting a locked initial session with a new auto-generated one

- **handleNewMessage()**:
  - Explicitly logs when using `opencodeSessionId` from request body vs inheriting from session
  - Ensures both paths are properly tracked

- **processMessage()**:
  - Uses `message.opencodeSessionId` if explicitly provided in the request
  - Falls back to `ensureOpencodeSession()` only if no explicit session ID
  - This ensures retried/continued messages use the same session

- **sendToOpencode()**:
  - Only updates session ID from captured events if no explicit session was passed
  - Prevents overwriting a passed `--session` arg with an auto-generated one

- **Session recovery section**:
  - Only sets `opencodeSessionId` to the detected ID if no session ID already exists
  - Preserves `initialOpencodeSessionId` and uses it as the final session ID
  - Added logging for both scenarios (new initial session detected vs preserving existing)

## How It Works Now

### First Message in a Session
1. Client sends message without `opencodeSessionId`
2. Server's `processMessage()` calls `ensureOpencodeSession()`
3. `ensureOpencodeSession()` creates a new OpenCode session (or lets CLI create one)
4. CLI reports session ID in JSON events
5. Server captures and stores it as `session.opencodeSessionId` and `session.initialOpencodeSessionId`
6. Session is now locked for all future messages

### Subsequent Messages
1. Client sends message **with** `opencodeSessionId` from `session.opencodeSessionId`
2. Server's `processMessage()` uses the explicit `message.opencodeSessionId`
3. `sendToOpencode()` is called with the explicit session ID
4. CLI runs with `--session <session-id>` argument
5. Same OpenCode session is used, maintaining conversation continuity

### Continue When Error / Retry
1. Original message fails, triggering `triggerModelFallback()`
2. Fallback creates a new message with `isContinuation: true`
3. New message includes the **original message's `opencodeSessionId`** in payload
4. Server uses the explicit session ID, continuing in the same OpenCode session
5. Conversation context is preserved across the error

### Redo Operations
1. **Proceed-with-build redo**: Uses `redoProceedWithBuild()` which preserves `session.opencodeSessionId`
2. **Regular opencode redo**: Uses `/api/sessions/:id/messages/:id/redo` endpoint which uses `session.opencodeSessionId`
3. Both maintain the same OpenCode session

## Key Changes

### Session ID Preservation
- **Never overwrite** `initialOpencodeSessionId` once it's set
- **Prefer explicit session IDs** from messages over auto-detected ones
- **Preserve across all operations**: send, redo, retry, continue

### Validation Failure Handling
- **Don't create new session** when validation fails
- **Return existing session** and let CLI handle it
- **Log extensively** for debugging session continuity issues

## Testing Checklist
- [ ] Send first message in a new session - should create new OpenCode session
- [ ] Send second message in same session - should reuse existing session
- [ ] Message fails and auto-retries - should continue in same session
- [ ] Click "Redo" on a message - should use same session
- [ ] Click "Proceed with Build" - should use same session
- [ ] Click "Redo Build" - should use same session
- [ ] Refresh page and send new message - should use existing session

## Files Modified
1. `chat/public/builder.js` - Client-side builder UI
2. `chat/public/app.js` - Client-side regular chat UI
3. `chat/server.js` - Server-side session management
