# Ticket 6.2 — Error Handling & Edge Cases

**Phase**: 6 — Testing & Polish
**Priority**: High
**Depends on**: All Phase 1-5 tickets

## Description

Comprehensive error handling for all failure modes. The relay should never crash from expected errors, should provide clear feedback to users, and should recover gracefully when possible.

## Acceptance Criteria

### AC1: OpenCode server not running

```gherkin
Given the OpenCode server is not running
When the relay starts
Then it attempts to start OpenCode via the process manager
And if it fails, it shows a clear error page to connected browsers:
  "OpenCode server is not running. Start it with: opencode serve"
And the relay stays alive (doesn't crash)
And it retries connection periodically
```

### AC2: OpenCode server crashes mid-session

```gherkin
Given the OpenCode server is running and processing a request
When the OpenCode process crashes unexpectedly
Then the relay detects the SSE connection drop
And it sends { type: "error", message: "OpenCode server disconnected" } to browsers
And it attempts to restart the OpenCode process
And on recovery, browsers are notified: "Reconnected to OpenCode"
```

### AC3: Browser disconnects and reconnects

```gherkin
Given a browser was connected and receiving events
When the browser loses connection (network drop, sleep, etc.)
And it reconnects within 5 minutes
Then it receives any pending permission requests
And it receives the current session state
And the chat shows events that were missed during disconnection
```

### AC4: Multiple browsers sending conflicting permissions

```gherkin
Given a permission request is pending
When browser A approves and browser B rejects simultaneously
Then only the first response is processed
And the second response is ignored (duplicate handling)
And both browsers see the same final decision
```

### AC5: Large tool results

```gherkin
Given a tool result contains more than 100KB of content
When it is sent to browsers
Then it is truncated to a reasonable size (e.g., first 50KB)
And a "Show full output" toggle is available
And the full content can be loaded on demand
```

### AC6: Invalid WebSocket messages

```gherkin
Given a browser sends malformed JSON
When the server receives it
Then it logs the error
And it sends { type: "error", message: "Invalid message format" } to that client
And it does NOT disconnect the client
And other clients are not affected
```

### AC7: Rate limiting on message sends

```gherkin
Given a user sends messages rapidly
When more than 5 messages are sent within 10 seconds
Then subsequent messages are queued (not dropped)
And the user sees "Message queued..." feedback
And messages are processed in order
```

### AC8: Disk space for session storage

```gherkin
Given session storage is using disk
When disk space is low (< 100MB free)
Then the relay logs a warning
And it continues operating but warns about potential data loss
And old session cache files are candidates for cleanup
```

## Testable Components

| Component | Test Type | What to Assert |
|-----------|-----------|----------------|
| OpenCode down | Fixture | Error page shown, retry in background |
| OpenCode crash recovery | Fixture | Detects, notifies, restarts, reconnects |
| Browser reconnection | Fixture | State replayed, pending items sent |
| Duplicate permission | Unit | First processed, second ignored |
| Large content truncation | Unit | Truncated at limit, expand available |
| Invalid JSON handling | Unit | Error sent, no disconnect |
| Message rate limit | Unit | Queued, processed in order |
| Disk space check | Unit | Warning logged at threshold |

## Notes

- Every error should be logged with enough context to debug
- User-facing errors should be human-readable (no stack traces)
- The relay should NEVER crash from an error in a single client connection
- Use process.on('uncaughtException') as a last resort safety net
