fix(state): keep /branch sessions visible after parent reopen (salvage #20864)#39214
Merged
kshitijk4poor merged 1 commit intoJun 4, 2026
Conversation
/branch (aka /fork) sessions vanished from /resume and /sessions. Both surfaces funnel through list_sessions_rich(include_children=False), which hid any session with a parent_session_id unless identified as a branch via a heuristic — parent.end_reason == 'branched' AND child.started_at >= parent.ended_at. Two ways that heuristic failed: 1. CLI/gateway branches: once the parent was reopened (e.g. resumed) and re-ended with a different end_reason (tui_shutdown overwriting 'branched'), the heuristic stopped matching and the branch was hidden permanently. 2. TUI branches (tui_gateway session.branch): the TUI never ends the parent as 'branched' — it creates the child while the parent is still live — so the heuristic NEVER matched and TUI branches were hidden from the moment they were created (this is the macOS desktop app's primary symptom). Fix: persist a stable '_branched_from' marker in the branch session's model_config at creation time across ALL THREE branch paths (CLI cli.py, gateway gateway/run.py, and TUI tui_gateway/server.py), and OR a json_extract(model_config, '$._branched_from') IS NOT NULL check into the list_sessions_rich filter. The marker is immutable across the parent's lifecycle, so the branch stays visible regardless of how/whether the parent is ended. The legacy end_reason heuristic is kept (OR'd) so pre-existing branches remain visible. Subagent/compression children (no marker, parent not 'branched') stay correctly hidden. Fixes NousResearch#20856. Approach by liuhao1024 (PR NousResearch#20864); reimplemented on current main, extended to the TUI branch path (which the original missed), with regression tests for the reopen+re-end scenario and the TUI marker persistence.
700bb1f to
0d281ca
Compare
12 tasks
1 task
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.
Summary
Salvages the substantive fix from #20864 by @liuhao1024 onto current
mainand closes #20856.The bug:
/branch(aka/fork) creates a new numbered branch session, but it vanishes from/resumeand/sessions. Both surfaces funnel throughlist_sessions_rich(include_children=False), which hides any session with aparent_session_idunless it's identified as a branch via a heuristic —parent.end_reason == 'branched' AND child.started_at >= parent.ended_at.That heuristic fails in two distinct ways:
reopen_session()clearsend_reasonand a laterend_session(parent, 'tui_shutdown')overwrites'branched'. After the parent is resumed once and re-closed (routine), the heuristic stops matching and the branch is hidden permanently.tui_gatewaysession.branch, the macOS desktop app's path) — the TUI never ends the parent as'branched'; it creates the child while the parent is still live. So the heuristic never matched and TUI branches were hidden from the moment they were created. This is almost certainly the primary user-visible symptom.The fix: persist a stable
_branched_frommarker in the branch session'smodel_configat creation time across all three branch paths (cli.py,gateway/run.py, andtui_gateway/server.py), and OR ajson_extract(model_config, '$._branched_from') IS NOT NULLcheck into thelist_sessions_richfilter. The marker is immutable across the parent's lifecycle, so the branch stays visible regardless of how (or whether) the parent is ended. The legacyend_reason='branched'heuristic is kept (OR'd) so branch sessions created before this change remain visible — no backward-compat regression. Subagent/compression children (no marker, parent not'branched') stay correctly hidden.Closes #20856.
Why a reimplementation rather than a cherry-pick
#20864 was a bundle of three unrelated fixes, 4826 commits behind
main, and conflicting. Triaged each:mainidentically (1a4e8f704). Dropped.requiredlist — marks mode-specific params as unconditionally required, contradicting its own "mode-specific" description; would break strict schema validators. Dropped./branchvisibility — the valuable one, salvaged here. The original PR only patched the CLI + gateway paths; this PR additionally covers the TUI path, which the original missed and which is the desktop app's primary surface.The original couldn't cherry-pick (the
/branchhandlers and thelist_sessions_richfilter all moved onmain), so @liuhao1024's_branched_fromapproach was reimplemented on currentmain. Commit is attributed to them; the TUI extension and regression tests are the maintainer follow-up.Verification
Reproduced the bug on current
mainacross all paths, then confirmed the fix end-to-end (isolatedHERMES_HOME, realSessionDB, and a handler-level E2E driving the actualsession.branchRPC):include_children=Truetests/test_hermes_state.py::...::test_branch_session_visible_after_parent_reopen_and_reend— fails onmain(assert 'branch' in ['parent']), passes here.tests/tui_gateway/test_protocol.py::test_session_branch_persists_branched_from_marker— fails onmain(KeyError: 'model_config'), passes here. Drives the realsession.branchhandler and asserts the marker is persisted.tests/tui_gateway/— 103 passed;tests/test_hermes_state.py+tests/cli/test_branch_command.py— 272 passed.ruff checkclean on all changed files.Credit
Approach and original fix by @liuhao1024 (#20864). Reimplemented on current
mainwith authorship preserved; TUI branch-path coverage + regression tests added by the maintainer.