Skip to content

Commit 7e20006

Browse files
nesquenaclaude
andcommitted
fix(models): make full-scan fallback path consistent with index path (#1171)
The PR removed the 60-second grace window from the index-path filter at api/models.py:558-567 but left the same filter at the full-scan fallback (line 589-594) with the grace window intact. The fallback path is only hit when SESSION_INDEX_FILE doesn't exist or fails to parse — rare in production but it's also the path used by tests/test_issue789.py (monkeypatch sets SESSION_INDEX_FILE to a temp path that's never created), which is why those existing assertions kept passing on the PR even though they assert the OLD "60s grace" behavior. Make both filter sites consistent: empty Untitled sessions are hidden regardless of age in BOTH paths. Update test_issue789.py assertions to reflect the new contract documented in the PR description ("a session only exists from the user's perspective once the first message is sent"). The two visibility-while-young tests are flipped to assert the new hidden behavior; the old-age tests already assert hidden so they continue to pass. Without this commit: - production: index path is always taken, PR works as documented - legacy installs (no _index.json): old grace behavior persists - tests: silently pass against the old fallback path, masking the inconsistency Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 parent 8c996f7 commit 7e20006

2 files changed

Lines changed: 34 additions & 26 deletions

File tree

api/models.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,11 +586,12 @@ def all_sessions():
586586
for s in SESSIONS.values():
587587
if all(s.session_id != x.session_id for x in out): out.append(s)
588588
out.sort(key=lambda s: (getattr(s, 'pinned', False), _session_sort_timestamp(s)), reverse=True)
589-
_now = time.time()
589+
# Hide empty Untitled sessions from the UI entirely — kept consistent with the
590+
# index-path filter above. No grace window: a 0-message Untitled session is
591+
# never shown regardless of age (#1171).
590592
result = [s.compact(include_runtime=True, active_stream_ids=active_stream_ids) for s in out if not (
591593
s.title == 'Untitled'
592594
and len(s.messages) == 0
593-
and (_now - s.updated_at) > 60
594595
)]
595596
result = [s for s in result if not _hide_from_default_sidebar(s)]
596597
for s in result:

tests/test_issue789.py

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
"""
22
Regression tests for GitHub issue #789.
33
4-
Bug: every brand-new session immediately disappeared from the sidebar because
5-
all_sessions() filtered out sessions where title == 'Untitled' AND
6-
message_count == 0. Since every new session starts with those values, it was
7-
filtered out of /api/sessions on the next refresh.
8-
9-
Fix: exempt sessions younger than 60 seconds from that filter. Sessions older
10-
than 60 seconds that are still Untitled with 0 messages are still suppressed
11-
(ghost sessions from test runs / accidental reloads).
4+
Original bug (#789): new sessions immediately disappeared because all_sessions()
5+
filtered out Untitled + 0-message sessions.
6+
7+
Original fix: exempt sessions younger than 60 seconds.
8+
9+
Updated for #1171 / #1182: a session only "exists" from the user's perspective
10+
once the first message is sent. Untitled + 0-message sessions are now hidden
11+
from the sidebar **regardless of age** — no grace window. The button guard
12+
(#1176) and the boot-restore guard (#1182) ensure the user is never locked
13+
out of typing into a fresh session, but the sidebar list never surfaces empty
14+
ones. These tests reflect the new contract.
1215
"""
1316
import json
1417
import time
@@ -67,34 +70,37 @@ def _make_titled_session(age_seconds, session_id=None):
6770
return s
6871

6972

70-
# ── Test 1: brand-new Untitled 0-message session IS included ─────────────────
73+
# ── Test 1: Untitled 0-message sessions are hidden regardless of age (#1171) ─
74+
75+
def test_new_untitled_session_is_hidden_from_sidebar():
76+
"""A brand-new (0 s old) Untitled 0-message session must NOT appear (#1171).
7177
72-
def test_new_untitled_session_is_visible_in_sidebar():
73-
"""A session created just now (0 seconds old) must appear in all_sessions()."""
78+
Updated for #1171/#1182: sessions only "exist" once the first message is
79+
sent. Empty scratch-pad sessions never surface in the sidebar.
80+
"""
7481
new_session = _make_untitled_session(age_seconds=0)
7582

7683
result = all_sessions()
7784
ids = {s["session_id"] for s in result}
7885

79-
assert new_session.session_id in ids, (
80-
"Brand-new Untitled 0-message session must be visible in the sidebar "
81-
"(fix for issue #789)"
86+
assert new_session.session_id not in ids, (
87+
"Untitled 0-message session must be hidden regardless of age (#1171)"
8288
)
8389

8490

85-
def test_recent_untitled_session_under_60s_is_visible():
86-
"""A session 30 seconds old should still be visible."""
91+
def test_recent_untitled_session_under_60s_is_hidden():
92+
"""A 30-second-old empty session must also be hidden (no grace window)."""
8793
recent_session = _make_untitled_session(age_seconds=30)
8894

8995
result = all_sessions()
9096
ids = {s["session_id"] for s in result}
9197

92-
assert recent_session.session_id in ids, (
93-
"Untitled 0-message session younger than 60 s must be visible (#789)"
98+
assert recent_session.session_id not in ids, (
99+
"Untitled 0-message session younger than 60 s is also hidden (#1171)"
94100
)
95101

96102

97-
# ── Test 2: old Untitled 0-message session IS still filtered ─────────────────
103+
# ── Test 2: old Untitled 0-message session is still filtered ─────────────────
98104

99105
def test_old_untitled_session_over_60s_is_filtered():
100106
"""A ghost session (Untitled, 0 messages, >60 s old) must be hidden."""
@@ -109,14 +115,14 @@ def test_old_untitled_session_over_60s_is_filtered():
109115

110116

111117
def test_session_exactly_at_boundary_is_filtered():
112-
"""A session just over 60 seconds old should be filtered."""
118+
"""A session at any age (the previous 60 s threshold no longer applies)."""
113119
boundary_session = _make_untitled_session(age_seconds=61)
114120

115121
result = all_sessions()
116122
ids = {s["session_id"] for s in result}
117123

118124
assert boundary_session.session_id not in ids, (
119-
"Untitled 0-message session older than 60 s must be filtered out"
125+
"Untitled 0-message session is filtered regardless of age (#1171)"
120126
)
121127

122128

@@ -181,14 +187,15 @@ def test_titled_session_with_no_messages_old_is_visible():
181187
# ── Test 4: mixed bag — only old Untitled empty sessions are filtered ─────────
182188

183189
def test_mixed_sessions_correct_visibility():
184-
"""With a mix of sessions, only old+Untitled+empty ones are suppressed."""
190+
"""With a mix of sessions, only sessions with messages OR titled sessions
191+
are surfaced (#1171). Both new and old Untitled+empty sessions are hidden."""
185192
new_ghost = _make_untitled_session(age_seconds=5, session_id="new_ghost")
186193
old_ghost = _make_untitled_session(age_seconds=200, session_id="old_ghost")
187194
real_session = _make_titled_session(age_seconds=500, session_id="real_session")
188195

189196
result = all_sessions()
190197
ids = {s["session_id"] for s in result}
191198

192-
assert "new_ghost" in ids, "New Untitled session (5s old) must be visible"
193-
assert "old_ghost" not in ids, "Old Untitled session (200s old) must be hidden"
199+
assert "new_ghost" not in ids, "New Untitled empty session is also hidden (#1171)"
200+
assert "old_ghost" not in ids, "Old Untitled session must be hidden"
194201
assert "real_session" in ids, "Titled session with messages must be visible"

0 commit comments

Comments
 (0)