This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/FORKING_IMPL.md
2026-02-21 16:53:07 +00:00

4.5 KiB
Raw Blame History

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)

  • Migration 013 (deleted_at, sibling_index)
  • tree.gogetActivePath, getActiveLeaf, getPathToLeaf, getSiblings, findLeafFromMessage, nextSiblingIndex, updateCursor
  • completion.goloadConversation walks active path via recursive CTE, persistMessage is cursor-aware with sibling_index, returns new message ID
  • messages.goGetActivePath, EditMessage, Regenerate (streaming + sibling), UpdateCursor, ListSiblings
  • Routes wired in main.go

Frontend (new work)

  • api.jsgetActivePath, editMessage, streamRegenerate, updateCursor, listSiblings
  • 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)
  • 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
  • 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 endpointListMessages 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 mutationreloadActivePath() 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