# Ticket 5.4 — Rewind / Revert Support

**Phase**: 5 — OpenCode-Specific Features
**Priority**: Medium
**Depends on**: 2.3, 4.1

## Description

Allow users to rewind a session to an earlier point, undoing agent actions (file changes, etc.). OpenCode provides revert and unrevert endpoints. The UI shows a preview of what will be undone before committing.

### OpenCode Revert API

- **Revert**: `POST /session/:id/revert` with `{ messageID, partID?, snapshot?, diff? }` — reverts to a specific message point
- **Unrevert**: `POST /session/:id/unrevert` — restores the session to pre-revert state
- **Diff**: `GET /session/:id/diff?messageID=` — returns `Snapshot.FileDiff[]` showing file changes since that message
- OpenCode uses `snapshot` and `patch` parts within messages to track file state, enabling accurate revert

Note: Claude-relay's rewind uses the SDK's `query.rewindFiles()` method and `resumeSessionAt` option. OpenCode's revert is simpler — it's a REST call that handles everything server-side.

## Acceptance Criteria

### AC1: Rewind button on messages

```gherkin
Given a session with messages that include file changes
When the user hovers over a message
Then a "Rewind to here" button appears
```

### AC2: Rewind preview

```gherkin
Given the user clicks "Rewind to here" on message #3
When the preview loads
Then a modal shows:
  - Messages that will be removed (4, 5, 6, ...)
  - File changes that will be undone (git diff style)
  - "Confirm Rewind" and "Cancel" buttons
```

### AC3: Rewind execution

```gherkin
Given the user clicks "Confirm Rewind"
When the revert is processed via POST /session/:id/revert
Then messages after the rewind point are removed from the chat
And file changes are undone
And the session state is updated
And an "Undo rewind" option appears
```

### AC4: Unrevert (undo rewind)

```gherkin
Given a rewind was just performed
When the user clicks "Undo rewind"
Then the session is restored to its pre-rewind state via POST /session/:id/unrevert
And the removed messages reappear
And file changes are reapplied
```

### AC5: Session diff display

```gherkin
Given a session has made file changes
When the user requests the session diff
Then it fetches from GET /session/:id/diff
And displays a git-style diff showing all changes made during the session
```

## Testable Components

| Component | Test Type | What to Assert |
|-----------|-----------|----------------|
| Revert API call | Unit | Constructs correct POST to /session/:id/revert |
| Unrevert API call | Unit | Constructs correct POST to /session/:id/unrevert |
| Rewind button | E2E | Appears on hover over eligible messages |
| Preview modal | E2E | Shows messages that will be removed and file diffs |
| Revert flow | E2E | Confirming revert removes messages, shows undo option |
| Unrevert flow | E2E | Clicking undo restores reverted messages |
| Diff display | E2E | Git-style diff rendered with correct colours and line numbers |

## Reference

- Claude-relay rewind: `../claude-relay/lib/public/modules/rewind.js`
- Claude-relay rewind preview: `../claude-relay/lib/project.js` (rewind_preview, rewind_execute)
- OpenCode revert: `POST /session/:id/revert`, `POST /session/:id/unrevert`
- OpenCode diff: `GET /session/:id/diff`
