Skip to content

feat(chat): switch between session transcript branches from the chat header#110857

Merged
steipete merged 2 commits into
mainfrom
claude/session-branch-switcher
Jul 18, 2026
Merged

feat(chat): switch between session transcript branches from the chat header#110857
steipete merged 2 commits into
mainfrom
claude/session-branch-switcher

Conversation

@steipete

Copy link
Copy Markdown
Contributor

What Problem This Solves

#110660 made rewind non-destructive: the abandoned transcript branch stays in the append-only session DAG. But nothing surfaced those branches, so an accidental rewind (or a change of heart) had no way back. Follow-up agreed in #110660 ("branch switcher / un-rewind").

Why This Change Was Made

  • New gateway RPCs: sessions.branches.list (operator.read) returns the transcript DAG's branch tips (headline from the branch's latest user/assistant message, message count, recency, active flag); sessions.branches.switch (operator.admin) repoints the active path to a chosen tip.
  • Switch generalizes the landed message-cut rotation machinery with a switch mode (full-DAG copy to a rotated session identity plus a trailing leaf control targeting the selected tip) instead of duplicating it — same lifecycle discipline as sessions.rewind: exclusive mutation, re-validation inside the lock, rejected while a run is active, queued sends cleared on success, external-harness-owned and legacy-JSONL sessions rejected with typed errors. Selecting the already-active branch is a typed error.
  • Tip computation handles the post-rewind shape: structural tips (entries no non-control entry references as parent) plus the current leaf-control-selected active leaf, which sits mid-DAG after a rewind.
  • Control UI: the chat title bar shows a branch menu (existing wa-dropdown, git-branch icon) only when more than one branch exists. Selecting an inactive branch switches and refreshes the thread via the same cache-clear + refetch pattern as rewind. Disabled with distinct tooltips while the agent is working or without operator-admin access. Branch data refreshes on session load, connection epoch change, transcript/session events, and after rewind/switch — no polling.
  • Protocol additions additive; no version bump. New CSS lives in the lazy chat chunk (startup budget untouched).

User Impact

Rewinds are now reversible from the UI: the chat header's branch menu lists every preserved transcript branch with its latest message and lets you switch back. Docs updated (docs/web/control-ui.md).

Evidence

  • node scripts/run-vitest.mjs src/config/sessions/session-accessor.sqlite-message-cut.test.ts — 12 tests: tip listing (active flag, headline, counts), switch repoint + projection, non-tip/unknown/already-active/unsupported-storage rejections.
  • node scripts/run-vitest.mjs src/gateway/server-methods/sessions-rewind.test.ts — 13 tests: list/switch handler shapes, busy and external-harness rejections, queue-clear symmetry.
  • node scripts/run-vitest.mjs ui/src/lib/sessions/message-cut.test.ts ui/src/pages/chat/chat-history.test.ts ui/src/pages/chat/components/chat-pane-header.test.ts — 30 tests: capability plumbing, switch flow cache-clear + refetch, header menu render/gating.
  • All focused suites re-run green after rebasing onto current origin/main (conflict resolution: docs bullet merge, header slot alongside the new faceControl element).
  • oxlint/oxfmt clean; pnpm ui:i18n:baseline no drift; fresh autoreview on the diff: clean, no actionable findings.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation app: web-ui App: web-ui gateway Gateway runtime size: XL maintainer Maintainer-authored PR labels Jul 18, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a77908e338

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +269 to +273
return tree.nodes.filter(
(node) =>
!isSessionTranscriptLeafControl(node.entry) &&
(node.id === tree.leafId || !referencedParents.has(node.id)),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude side append artifacts from branch tips

In sessions that contain side appends, this filter treats any non-leaf node without a child as a switchable transcript branch. Side artifacts are real today (for example src/auto-reply/reply/conversation-turn-capture.ts appends custom events with appendMode: "side"), and transcript-tree explicitly keeps those side branches out of prompts; listing them here both shows fake branches in the chat header and lets sessions.branches.switch repoint the active leaf to an audit/side artifact. Please exclude appendMode: "side" entries (or otherwise restrict this to visible conversation branch leaves) before using the same set for listing and validation.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jul 18, 2026
@clawsweeper

clawsweeper Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 18, 2026, 2:17 PM ET / 18:17 UTC.

Summary
This PR adds additive gateway RPCs and Control UI chat-header controls for listing and switching preserved SQLite session transcript branches.

PR surface: Source +739, Tests +320, Docs 0. Total +1059 across 26 files.

Reproducibility: yes. from source inspection: append a custom transcript event using appendMode: "side", then invoke the new branch list/switch path. The reviewed implementation’s structural-tip predicate makes that side artifact eligible despite the transcript tree excluding it from conversation paths.

Review metrics: 1 noteworthy metric.

  • Gateway surface: 2 additive RPCs, 1 state-changing. The new public list/switch contract exposes persisted transcript-branch semantics, so the eligible-leaf boundary needs to be correct before clients rely on it.

Stored data model
Persistent data-model change detected: serialized state: packages/gateway-protocol/src/schema/sessions.ts, serialized state: src/config/sessions/session-accessor.message-cut.ts, serialized state: src/config/sessions/session-accessor.sqlite-message-cut.test.ts, serialized state: src/config/sessions/session-accessor.sqlite-message-cut.ts, serialized state: src/config/sessions/session-accessor.ts, serialized state: src/config/sessions/session-accessor.types.ts, and 10 more. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Exclude side append artifacts from the gateway’s eligible branch-tip set and cover it with a regression test.
  • [P1] Add redacted real behavior proof showing rewind, the branch menu, and a switch restoring the intended transcript branch.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body documents focused automated tests and static checks, but no inspectable after-fix live branch-switching run is provided; add a redacted Control UI recording, terminal transcript, or runtime log, then update the PR body for re-review.

Mantis proof suggestion
A redacted web UI chat transcript would directly demonstrate the new header menu and whether switching restores the selected preserved conversation path. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis web UI chat proof: reproduce a rewind, switch to the preserved branch, and capture a redacted transcript showing the header menu and restored thread.

Risk before merge

  • [P1] Until side-appended artifacts are excluded from selectable tips, a user can see a non-conversation artifact in the branch menu and switch the active transcript path to it, creating incorrect session state despite the otherwise valid rotation lifecycle.

Maintainer options:

  1. Constrain switchable transcript leaves (recommended)
    Exclude side-appended artifacts from list and switch validation, cover the case with a focused regression test, and refresh proof before merge.
  2. Pause the feature
    Pause this branch-switching API/UI until its selectable-leaf contract is explicitly separated from audit and side-event branches.

Next step before merge

  • [P1] The PR carries the protected maintainer label and also needs contributor-supplied real behavior proof; the concrete code fix should be resolved in normal maintainer/contributor review rather than an automated repair lane.

Security
Cleared: The reviewed diff adds authenticated gateway methods under existing operator.read and operator.admin scopes and shows no dependency, workflow, secret, or supply-chain change.

Review findings

  • [P2] Exclude side append artifacts from switchable branch tips — src/config/sessions/session-accessor.sqlite-message-cut.ts:1-164
Review details

Best possible solution:

Restrict branch enumeration and switch validation to conversation-visible leaves, explicitly excluding appendMode: "side" artifacts, add a side-append regression fixture, and then provide redacted after-fix UI or terminal evidence of a real rewind-and-switch flow.

Do we have a high-confidence way to reproduce the issue?

Yes, from source inspection: append a custom transcript event using appendMode: "side", then invoke the new branch list/switch path. The reviewed implementation’s structural-tip predicate makes that side artifact eligible despite the transcript tree excluding it from conversation paths.

Is this the best way to solve the issue?

No. Generalizing the message-cut lifecycle is a sound direction, but its candidate set must be the visible conversation leaves rather than every non-control structural leaf; filtering only in the UI would leave the gateway mutation unsafe.

Full review comments:

  • [P2] Exclude side append artifacts from switchable branch tips — src/config/sessions/session-accessor.sqlite-message-cut.ts:1-164
    The structural-tip set also admits appendMode: "side" artifacts. Those events are intentionally excluded from prompt conversation paths, but this code lists them in the header and accepts them for sessions.branches.switch, so a user can select an audit/side event as the active transcript leaf. Filter candidates to conversation-visible leaves in both listing and validation, and add a side-append regression case. This repeats the existing review concern because the current head is unchanged.
    Confidence: 0.98

Overall correctness: patch is incorrect
Overall confidence: 0.98

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against b7f99d0ffd2a.

Label changes

Label changes:

  • add P2: This is a bounded but user-visible session-history correctness defect in a new Control UI feature.
  • add merge-risk: 🚨 session-state: The current implementation can make a non-conversational side artifact an active transcript branch.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body documents focused automated tests and static checks, but no inspectable after-fix live branch-switching run is provided; add a redacted Control UI recording, terminal transcript, or runtime log, then update the PR body for re-review.

Label justifications:

  • P2: This is a bounded but user-visible session-history correctness defect in a new Control UI feature.
  • merge-risk: 🚨 session-state: The current implementation can make a non-conversational side artifact an active transcript branch.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body documents focused automated tests and static checks, but no inspectable after-fix live branch-switching run is provided; add a redacted Control UI recording, terminal transcript, or runtime log, then update the PR body for re-review.
Evidence reviewed

PR surface:

Source +739, Tests +320, Docs 0. Total +1059 across 26 files.

View PR surface stats
Area Files Added Removed Net
Source 20 793 54 +739
Tests 5 334 14 +320
Docs 1 1 1 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 26 1128 69 +1059

What I checked:

  • Unresolved branch-tip defect: The current PR head remains a77908e, the same revision targeted by the existing review. That review identifies that structural tip selection includes appendMode: "side" artifacts, while current conversation-turn capture writes side custom events; the branch list can therefore display and switch to an audit artifact rather than a visible conversation leaf. (src/config/sessions/session-accessor.sqlite-message-cut.ts, a77908e3386c)
  • Sibling behavior contract: The prior review cites transcript-tree behavior that excludes side branches from prompt construction, so reusing a broader structural-tip set for user-visible branch switching violates the established conversation-path boundary. (src/config/sessions/transcript-tree.ts, b7f99d0ffd2a)
  • Feature provenance: The merged predecessor introduced non-destructive rewind/fork and explicitly described a future branch-switcher follow-up; this PR is a distinct UI/API continuation rather than a duplicate of that merged work. (src/config/sessions/session-accessor.sqlite-message-cut.ts, 0acece45912d)
  • Real-proof gap: The PR body reports focused tests, formatting, and type checks, but it does not include inspectable after-fix live UI, terminal, log, recording, or artifact evidence for switching a real preserved branch. (a77908e3386c)

Likely related people:

  • steipete: Authored the merged rewind/fork implementation that established the append-only transcript rotation and the branch-switcher follow-up this PR extends. (role: recent message-cut feature contributor; confidence: high; commits: 0acece45912d, 31322034554c; files: src/config/sessions/session-accessor.sqlite-message-cut.ts, src/gateway/server-methods/sessions-rewind.ts, ui/src/pages/chat/chat-history.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@openclaw-barnacle openclaw-barnacle Bot added the app: android App: android label Jul 18, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1021407619

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 1084 to +1086
const matchesChat = sessionMessageMatchesChat(state, event);
if (matchesChat) {
void loadChatBranches(state);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Coalesce branch refreshes across transcript events

For long or tool-heavy sessions, every session.message event starts another sessions.branches.list request, while that handler loads and parses the entire unbounded transcript (src/config/sessions/session-accessor.sqlite-message-cut.ts:84) and walks its branch paths. chatBranchRequestVersions only prevents stale results from applying; it does not deduplicate or cancel the synchronous Gateway work, so a burst of persisted tool/message events repeatedly scans the same growing transcript and can block the Gateway event loop. Refresh once after the run settles or coalesce concurrent refreshes per session.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit 5fdb0cd into main Jul 18, 2026
131 of 133 checks passed
@steipete
steipete deleted the claude/session-branch-switcher branch July 18, 2026 19:06
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 19, 2026
…header (openclaw#110857)

* feat(chat): switch between session transcript branches from the chat header

* fix(chat): type fixes and protocol regen for branch switcher CI
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android app: web-ui App: web-ui docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XL status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant