Bug Description
After running /branch (aka /fork) to create a forked session, the branched session disappears from hermes sessions list and the TUI resume list once the parent session is closed via TUI shutdown.
Reproduction Steps
- Start a session in the TUI
- Run
/branch my-experiment
- Verify the fork is visible (
hermes sessions list works because parent still has end_reason="branched")
- Close the TUI (ctrl+d or /quit)
- Restart the TUI — the forked session does not appear in any listing
hermes sessions list --limit 20 — fork is absent
- But it is in the DB:
SELECT id, title FROM sessions WHERE parent_session_id IS NOT NULL shows it with 188 messages and a valid title
Root Cause
Two interacting conditions in list_sessions_rich() (hermes_state.py lines 997–1003):
where_clauses.append(
"(s.parent_session_id IS NULL"
" OR EXISTS (SELECT 1 FROM sessions p"
" WHERE p.id = s.parent_session_id"
" AND p.end_reason = 'branched'"
" AND s.started_at >= p.ended_at))"
)
Issue 1: p.end_reason = 'branched' gets overwritten
When /branch runs, cli.py:_handle_branch_command() calls end_session(parent_id, "branched"), setting end_reason="branched". But if the user later resumes the parent session via the TUI, tui_gateway/server.py:2119 calls reopen_session(), which clears ended_at and end_reason. When the TUI eventually shuts down, _finalize_session() calls end_session(session_key, "tui_shutdown"), overwriting to "tui_shutdown". Now the filter fails.
Issue 2: s.started_at >= p.ended_at is wrong for /branch semantics
/branch forks the session while the parent is still live — the agent continues running in the new branch immediately. So child.started_at < parent.ended_at (child starts before parent ends). The filter requires child.started_at >= parent.ended_at, which fails for all /branch-created sessions.
Evidence from DB
Parent session: 20260506_173149_af6684
end_reason: tui_shutdown (was "branched", overwritten on TUI close)
ended_at: 18:59:08 (parent re-ended after being resumed)
Fork session: 20260506_182718_932e89 (188 messages, titled "Hermes In-Process Cron Architecture #2")
started_at: 18:27:18 (child starts BEFORE parent ends — expected for /branch)
ended_at: 18:58:12
Filter check:
p.end_reason = 'branched'? → 'tui_shutdown' ≠ 'branched' ✗
s.started_at >= p.ended_at? → 18:27:18 >= 18:59:08? ✗
Both conditions fail, so the fork is excluded from ALL session listings.
Impact
- Forked/branched sessions become completely invisible through normal UX
- Users cannot find or resume their branched work
- Data is not lost (still in SQLite) but unreachable without direct DB queries or
--resume <session_id>
- Particularly harmful for long troubleshooting sessions where forking is the intended pattern
Suggested Fix Approaches
Option A — The filter needs to recognise /branch children independently of the parent's end_reason. The cleanest fix: change the include_children filter to track branches via a separate mechanism (e.g. an explicit branch_of column or a branched flag on the child session), rather than relying on fragile heuristics over the parent's end_reason and timestamps.
Option B — A simpler band-aid: change the condition from p.end_reason = 'branched' to p.end_reason IN ('branched', 'tui_shutdown') and remove the s.started_at >= p.ended_at requirement. This would be more permissive but carries risk of surfacing compression-continuation and subagent sessions that the original filter was designed to hide. Better to add a session provenance field.
Option C — In _handle_branch_command(), mark the child session (not the parent) with a provenance flag (e.g. a session_meta key {"branched_from": "parent_id"}), and add that to the default-include list in list_sessions_rich.
Environment
- Hermes Agent (git-installed, latest)
- TUI mode (
hermes --tui)
- DeepSeek V4 Flash provider
- WSL2 (Ubuntu 24.04)
Bug Description
After running
/branch(aka/fork) to create a forked session, the branched session disappears fromhermes sessions listand the TUI resume list once the parent session is closed via TUI shutdown.Reproduction Steps
/branch my-experimenthermes sessions listworks because parent still hasend_reason="branched")hermes sessions list --limit 20— fork is absentSELECT id, title FROM sessions WHERE parent_session_id IS NOT NULLshows it with 188 messages and a valid titleRoot Cause
Two interacting conditions in
list_sessions_rich()(hermes_state.pylines 997–1003):Issue 1:
p.end_reason = 'branched'gets overwrittenWhen
/branchruns,cli.py:_handle_branch_command()callsend_session(parent_id, "branched"), settingend_reason="branched". But if the user later resumes the parent session via the TUI,tui_gateway/server.py:2119callsreopen_session(), which clearsended_atandend_reason. When the TUI eventually shuts down,_finalize_session()callsend_session(session_key, "tui_shutdown"), overwriting to"tui_shutdown". Now the filter fails.Issue 2:
s.started_at >= p.ended_atis wrong for/branchsemantics/branchforks the session while the parent is still live — the agent continues running in the new branch immediately. Sochild.started_at < parent.ended_at(child starts before parent ends). The filter requireschild.started_at >= parent.ended_at, which fails for all/branch-created sessions.Evidence from DB
Both conditions fail, so the fork is excluded from ALL session listings.
Impact
--resume <session_id>Suggested Fix Approaches
Option A — The filter needs to recognise
/branchchildren independently of the parent's end_reason. The cleanest fix: change theinclude_childrenfilter to track branches via a separate mechanism (e.g. an explicitbranch_ofcolumn or abranchedflag on the child session), rather than relying on fragile heuristics over the parent's end_reason and timestamps.Option B — A simpler band-aid: change the condition from
p.end_reason = 'branched'top.end_reason IN ('branched', 'tui_shutdown')and remove thes.started_at >= p.ended_atrequirement. This would be more permissive but carries risk of surfacing compression-continuation and subagent sessions that the original filter was designed to hide. Better to add a session provenance field.Option C — In
_handle_branch_command(), mark the child session (not the parent) with a provenance flag (e.g. asession_metakey{"branched_from": "parent_id"}), and add that to the default-include list inlist_sessions_rich.Environment
hermes --tui)