Changeset 0.8.0 (#39)
This commit is contained in:
96
FORKING_IMPL.md
Normal file
96
FORKING_IMPL.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Message Editing & Forking — Implementation
|
||||
|
||||
**Version:** 0.7.x (Phase 2 per ARCHITECTURE.md §8)
|
||||
**Depends on:** parent_id (006), channel_cursors (008) — both deployed
|
||||
|
||||
---
|
||||
|
||||
## Status: Complete (backend + frontend)
|
||||
|
||||
### Backend (already existed)
|
||||
- [x] Migration 013 (`deleted_at`, `sibling_index`)
|
||||
- [x] `tree.go` — `getActivePath`, `getActiveLeaf`, `getPathToLeaf`, `getSiblings`,
|
||||
`findLeafFromMessage`, `nextSiblingIndex`, `updateCursor`
|
||||
- [x] `completion.go` — `loadConversation` walks active path via recursive CTE,
|
||||
`persistMessage` is cursor-aware with sibling_index, returns new message ID
|
||||
- [x] `messages.go` — `GetActivePath`, `EditMessage`, `Regenerate` (streaming + sibling),
|
||||
`UpdateCursor`, `ListSiblings`
|
||||
- [x] Routes wired in `main.go`
|
||||
|
||||
### Frontend (new work)
|
||||
- [x] `api.js` — `getActivePath`, `editMessage`, `streamRegenerate`, `updateCursor`, `listSiblings`
|
||||
- [x] `app.js` — tree-aware message flow:
|
||||
- `selectChat` uses `/path` endpoint
|
||||
- `sendMessage` reloads path after streaming for server-authoritative IDs
|
||||
- `regenerateMessage(id)` creates sibling via message-specific endpoint
|
||||
- `editMessage(id)` → inline edit → `submitEdit` → sibling + completion
|
||||
- `switchSibling(id, direction)` navigates branches via cursor update
|
||||
- `reloadActivePath()` refreshes from server after every mutation
|
||||
- Legacy `regenerate()` button delegates to `regenerateMessage(lastAssistantId)`
|
||||
- [x] `ui.js`:
|
||||
- `_messageHTML` renders ‹ 1/3 › branch indicators at fork points
|
||||
- Edit button on user messages, Regen button on assistant messages
|
||||
- `showEditInline` — textarea with Ctrl+Enter submit, Escape cancel
|
||||
- [x] `styles.css` — `.branch-nav`, `.branch-arrow`, `.branch-pos`,
|
||||
`.msg-edit-input`, `.msg-edit-actions`, `.msg-edit-cancel`, `.msg-edit-submit`
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| `GET` | `/channels/:id/path` | Active path with sibling metadata |
|
||||
| `POST` | `/channels/:id/messages/:msgId/edit` | Create sibling user message |
|
||||
| `POST` | `/channels/:id/messages/:msgId/regenerate` | Create sibling assistant response (SSE) |
|
||||
| `PUT` | `/channels/:id/cursor` | Switch active branch |
|
||||
| `GET` | `/channels/:id/messages/:msgId/siblings` | List siblings at a fork point |
|
||||
|
||||
---
|
||||
|
||||
## Design Decisions
|
||||
|
||||
1. **`sibling_index` column** — explicit integer, not derived from `created_at`. Tree is self-describing.
|
||||
2. **New `/path` endpoint** — `ListMessages` retained for admin/debug. Frontend uses `/path` exclusively.
|
||||
3. **Two-step edit** — API: edit creates sibling, completion is separate. Frontend chains them.
|
||||
4. **Uniform ‹ › arrows** — same UI regardless of how fork was created (edit, regen, explicit).
|
||||
5. **Message-specific regenerate** — replaces old channel-level stub. Bottom-bar button delegates.
|
||||
6. **Branch switch → leaf** — clicking mid-tree sibling walks to deepest descendant via `findLeafFromMessage`.
|
||||
7. **Path reload after every mutation** — `reloadActivePath()` called post-send, edit, regen, abort.
|
||||
|
||||
---
|
||||
|
||||
## User Flows
|
||||
|
||||
### Edit a message
|
||||
1. Hover user message → click **Edit**
|
||||
2. Textarea replaces message text (Ctrl+Enter to submit, Escape to cancel)
|
||||
3. Submit → `POST /messages/:id/edit` (creates sibling) → `POST /chat/completions` (new response)
|
||||
4. Branch indicator appears: ‹ 1/2 ›
|
||||
5. Old branch preserved, navigable via arrows
|
||||
|
||||
### Regenerate a response
|
||||
1. Hover assistant message → click **Regen** (or bottom-bar ↻ button)
|
||||
2. `POST /messages/:id/regenerate` streams new response as sibling
|
||||
3. Branch indicator appears: ‹ 1/2 ›
|
||||
4. Original response preserved, navigable via arrows
|
||||
|
||||
### Navigate branches
|
||||
1. Click ‹ or › on any fork point
|
||||
2. `GET /messages/:id/siblings` → pick adjacent → `PUT /cursor` with sibling ID
|
||||
3. Backend walks to leaf, returns new path
|
||||
4. Entire conversation below fork point updates to show selected branch
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Existing linear conversations load and render correctly (backward compat)
|
||||
- [ ] New messages in linear conversation work as before
|
||||
- [ ] Regen creates sibling, shows ‹ 1/2 ›, original preserved
|
||||
- [ ] Multiple regens show ‹ 1/3 ›, all navigable
|
||||
- [ ] Edit creates sibling user message + triggers new completion
|
||||
- [ ] Branch arrows navigate correctly (← older, → newer)
|
||||
- [ ] Context sent to LLM is active path only (no cross-branch pollution)
|
||||
- [ ] Cursor persists across page reload (reopening chat shows last-viewed branch)
|
||||
- [ ] Mobile: edit textarea usable, branch arrows tappable
|
||||
Reference in New Issue
Block a user