From 105bb91418796ca3f4e3570e60d0f9e8826c8267 Mon Sep 17 00:00:00 2001 From: liuhao1024 Date: Wed, 16 Sep 2026 12:40:29 -0700 Subject: [PATCH] fix(state): surface archived+hidden sessions in the archived-only listing The archived-only view is the recovery surface for sessions that dropped out of every default list. `list_sessions_rich(archived_only=True)` still appended `s.hidden = 0`, so a row that was both archived and hidden (Bot Mode marks its sessions hidden) appeared in no UI list at all and could only be recovered with direct DB access. Skip the hidden filter when the caller asks for archived rows only; the default list keeps excluding hidden rows. Fixes #90946 --- hermes_state_sessions.py | 6 +++++- tests/hermes_state/test_session_archiving.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/hermes_state_sessions.py b/hermes_state_sessions.py index 2f2c6b2e9d..1e44841716 100644 --- a/hermes_state_sessions.py +++ b/hermes_state_sessions.py @@ -1218,7 +1218,11 @@ class SessionSessionsMixin: exclude_sources=exclude_sources, cwd_prefix=cwd_prefix, min_message_count=min_message_count, archived_only=archived_only, include_archived=include_archived, ) - if not include_hidden: + # The archived-only view is the recovery surface for rows that dropped out of every + # default list: a session that is archived AND hidden (Bot Mode marks its sessions + # hidden) must still be reachable there, or nothing but direct DB access can bring + # it back (#90946). + if not include_hidden and not archived_only: where_clauses.append("s.hidden = 0") where_sql = _where_sql(where_clauses) base_where_params = list(params) # pinned back-fill reuses the WHERE before LIMIT/OFFSET diff --git a/tests/hermes_state/test_session_archiving.py b/tests/hermes_state/test_session_archiving.py index 36ecb95a17..2d5fbac1eb 100644 --- a/tests/hermes_state/test_session_archiving.py +++ b/tests/hermes_state/test_session_archiving.py @@ -49,3 +49,18 @@ def test_unarchiving_compression_tip_unarchives_projected_root(db): assert db.get_session("root")["archived"] == 0 assert db.get_session("tip")["archived"] == 0 assert [s["id"] for s in db.list_sessions_rich(order_by_last_active=True)] == ["tip"] + + +def test_archived_only_view_includes_hidden_archived_sessions(db): + """The archived-only view is the recovery surface: a session that is both + archived and hidden (Bot Mode marks its sessions hidden) must appear + there, otherwise it is unreachable from every UI list (#90946).""" + db.create_session("plain", source="cli") + db.create_session("both", source="cli") + assert db.set_session_hidden("both", True) is True + assert db.set_session_archived("both", True) is True + + # Default list: hidden rows stay excluded (unchanged behaviour)... + assert [s["id"] for s in db.list_sessions_rich(order_by_last_active=True)] == ["plain"] + # ...and the archived-only view must surface the archived+hidden row. + assert [s["id"] for s in db.list_sessions_rich(order_by_last_active=True, archived_only=True)] == ["both"]