Fork this chat — branch conversations on both backends, with full UI - #416
Open
pufit wants to merge 1 commit into
Open
Fork this chat — branch conversations on both backends, with full UI#416pufit wants to merge 1 commit into
pufit wants to merge 1 commit into
Conversation
- claude: record per-turn transcript uuid as native_turn_id, pass resume_session_at for message-anchored forks (codex parity) - fork_session: validate anchor + native conversation up front, copy source messages up to the fork boundary for display parity - UI: header Fork button, hover fork-from-here on messages, sidebar kebab Fork, fork provenance banner, branch pills at divergence points, cmd+shift+F
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A complete "fork this chat" feature: branch any conversation into a new independent chat — from its end or from any earlier message — on both agent backends (Claude and Codex).
The backend already had fork scaffolding (
POST /api/sessions/fork, WSfork, codexthread/fork+lastTurnId), but Claude couldn't fork mid-conversation, forks started visually empty, invalid forks failed only on their first message, and nothing in the UI exposed any of it.Backend
ClaudeClientnow records each turn's last main-chain transcript-entry uuid and emits it asTurnCompleted.native_turn_id(exact codex parity — same column, same mapping helper). Message-anchored forks pass it asresume_session_atalongsidefork_session(gated onspec.forkso it can never truncate the original in place).fork_session()copies the source's message rows up to the fork boundary into the fork (db.copy_messages_to_session) — the forked chat opens showing exactly the history the agent remembers. Copies keepcreated_atandnative_turn_id(forks are re-forkable) and getexternal_id="forkcopy:<src id>"for provenance.PATCH /api/sessions/{id}: un-nesting a not-yet-started fork is refused (it would silently discard the pending fork context).UI/UX
native_turn_idpresent in the transcript), so it never promises a branch it can't make.⌘⇧F— fork the whole chat; shown once the session has a native conversation.parent_session_idnesting).⑂ <fork title>pills linking to each branch.Verified
pytest tests/— 3367 passed (new coverage: copy boundaries incl. user-message anchors, unmapped-anchor refusal, no-conversation refusal, claude uuid capture incl. sidechain exclusion, per-turn anchor reset,resume_session_atgating).npm run build+ vitest (229 passed; the one failing suite is a pre-existing collection failure on main), eslint clean on touched files.Notes
native_turn_idsince v039.native_turn_idcolumn and v039 indexes.