fix: show cron sessions in project filter

This commit is contained in:
AJV20
2026-05-28 08:04:35 -04:00
parent 5528e2c579
commit 9e69db9920
5 changed files with 92 additions and 6 deletions

View File

@@ -3,6 +3,10 @@
## [Unreleased]
### Fixed
- Cron sessions assigned to the dedicated Cron Jobs project now remain hidden from the default sidebar while still appearing when that project chip is selected.
## [v0.51.152] — 2026-05-28 — Release DX (stage-batch34 — single-PR optional gateway-backed browser chat)
### Added

View File

@@ -2228,6 +2228,38 @@ def _is_intentionally_background_sidebar_session(session: dict) -> bool:
return source == 'cron' or sid.startswith('cron_')
def _include_project_hidden_background_sidebar_sessions(
candidates: list[dict],
visible: list[dict],
) -> list[dict]:
"""Keep project-assigned background sessions addressable by project chips.
Cron sessions stay hidden from the default sidebar, but if they have a
project assignment they must still be present in the client cache so the
dedicated project chip can reveal them (#3019).
"""
visible_ids = {
str(session.get('session_id'))
for session in visible
if session.get('session_id')
}
out = list(visible)
for session in candidates:
sid = str(session.get('session_id') or '')
if not sid or sid in visible_ids:
continue
if not _is_intentionally_background_sidebar_session(session):
continue
if not session.get('project_id'):
continue
if _sidebar_message_count(session) <= 0:
continue
row = dict(session)
row['default_hidden'] = True
out.append(row)
return out
def _preserve_messageful_sidebar_discoverability(
candidates: list[dict],
visible: list[dict],
@@ -2503,8 +2535,10 @@ def all_sessions(diag=None):
and not s.get('worktree_path')
)]
result = _prefer_fuller_snapshots_for_sidebar(result)
visible_result = [s for s in result if not _hide_from_default_sidebar(s)]
result = _preserve_messageful_sidebar_discoverability(result, visible_result)
sidebar_candidates = result
visible_result = [s for s in sidebar_candidates if not _hide_from_default_sidebar(s)]
result = _preserve_messageful_sidebar_discoverability(sidebar_candidates, visible_result)
result = _include_project_hidden_background_sidebar_sessions(sidebar_candidates, result)
_strip_sidebar_internal_flags(result)
# Backfill: sessions created before Sprint 22 have no profile tag.
# Attribute them to 'default' so the client profile filter works correctly.
@@ -2542,8 +2576,10 @@ def all_sessions(diag=None):
and not getattr(s, 'worktree_path', None)
)]
result = _prefer_fuller_snapshots_for_sidebar(result)
visible_result = [s for s in result if not _hide_from_default_sidebar(s)]
result = _preserve_messageful_sidebar_discoverability(result, visible_result)
sidebar_candidates = result
visible_result = [s for s in sidebar_candidates if not _hide_from_default_sidebar(s)]
result = _preserve_messageful_sidebar_discoverability(sidebar_candidates, visible_result)
result = _include_project_hidden_background_sidebar_sessions(sidebar_candidates, result)
_strip_sidebar_internal_flags(result)
for s in result:
if not s.get('profile'):

View File

@@ -3285,7 +3285,9 @@ function renderSessionListFromCache(){
// in _profiles_match, and a strict-equality client filter would reject those
// rows incorrectly. So we trust the wire data and skip the redundant client
// filter entirely.
const profileFiltered=sourceFiltered;
const profileFiltered=sourceFiltered.filter(s=>
!s.default_hidden||(_activeProject&&_activeProject!==NO_PROJECT_FILTER&&s.project_id===_activeProject)
);
// Filter by active project. NO_PROJECT_FILTER sentinel asks for sessions
// with no project_id; otherwise filter to the matching project_id, or
// pass through when no filter is active.

View File

@@ -0,0 +1,44 @@
"""Regression tests for #3019 cron sessions under the Cron Jobs project."""
def test_project_assigned_cron_rows_are_returned_but_default_hidden():
from api.models import _include_project_hidden_background_sidebar_sessions
visible = [
{"session_id": "webui-1", "title": "Normal", "message_count": 1, "project_id": None},
]
candidates = visible + [
{
"session_id": "cron_visible",
"source_tag": "cron",
"title": "Cron output",
"message_count": 2,
"project_id": "cron-project",
},
{
"session_id": "cron_unassigned",
"source_tag": "cron",
"title": "Cron output",
"message_count": 2,
"project_id": None,
},
{
"session_id": "cron_empty",
"source_tag": "cron",
"title": "Cron output",
"message_count": 0,
"project_id": "cron-project",
},
]
rows = _include_project_hidden_background_sidebar_sessions(candidates, visible)
by_id = {row["session_id"]: row for row in rows}
assert set(by_id) == {"webui-1", "cron_visible"}
assert by_id["cron_visible"]["default_hidden"] is True
def test_session_list_project_filter_can_reveal_default_hidden_cron_rows():
src = ( __import__("pathlib").Path(__file__).parent.parent / "static" / "sessions.js").read_text(encoding="utf-8")
assert "!s.default_hidden||(_activeProject&&_activeProject!==NO_PROJECT_FILTER&&s.project_id===_activeProject)" in src

View File

@@ -111,7 +111,7 @@ class TestSidebarFirstTurnVisibility:
def test_backend_index_filter_keeps_pending_first_turn_sessions(self):
src = read("api/models.py")
index_filter_start = src.index("# Hide empty Untitled sessions from the UI entirely")
index_filter_end = src.index("result = [s for s in result if not _hide_from_default_sidebar", index_filter_start)
index_filter_end = src.index("visible_result = [s for s in sidebar_candidates if not _hide_from_default_sidebar", index_filter_start)
index_filter = src[index_filter_start:index_filter_end]
assert "has_pending_user_message" in index_filter, (
"The index-path empty-session filter must exempt pending first-turn sessions, "