From c507684b101b1051cb0c121b5d8932b1fe4e17f7 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 12 Jun 2026 09:14:23 -0400 Subject: [PATCH 1/7] fix(sidebar): align session source counts with rendered rows (#3966) --- static/sessions.js | 32 +++++++++++++++++++-- tests/test_issue3966_session_count_label.py | 32 +++++++++++++++++++++ tests/test_sidebar_session_partition.py | 12 ++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/test_issue3966_session_count_label.py diff --git a/static/sessions.js b/static/sessions.js index 485132f6..56c8d410 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -4766,6 +4766,28 @@ function _partitionSidebarSessionRows(allMatched, activeSidForSidebar){ }; } +function _renderSidebarRowsFromRawSessions(sessionsRaw){ + return _attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw); +} + +function _countRenderedSidebarRows(allMatched, activeSidForSidebar, showCliOnly){ + const sessionsRaw=[]; + for(const s of allMatched){ + if(!_sidebarRowHasVisibleMessages(s, activeSidForSidebar)) continue; + const isCli=_isCliSession(s); + if(showCliOnly ? !isCli : isCli) continue; + if(s.default_hidden&&!(_activeProject&&_activeProject!==NO_PROJECT_FILTER&&s.project_id===_activeProject)) continue; + if(_activeProject===NO_PROJECT_FILTER){ + if(s.project_id) continue; + } else if(_activeProject){ + if(s.project_id!==_activeProject) continue; + } + if(!_showArchived&&s.archived) continue; + sessionsRaw.push(s); + } + return _renderSidebarRowsFromRawSessions(sessionsRaw).length; +} + function renderSessionListFromCache(){ // Don't re-render while user is actively renaming a session (would destroy the input) if(_renamingSid) return; @@ -4795,7 +4817,13 @@ function renderSessionListFromCache(){ sessionsRaw, archivedCount, }=_partitionSidebarSessionRows(allMatched, activeSidForSidebar); - const sessions=_attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw); + const sessions=_renderSidebarRowsFromRawSessions(sessionsRaw); + const renderedWebuiSessionCount=_sessionSourceFilter==='webui' + ? sessions.length + : _countRenderedSidebarRows(allMatched, activeSidForSidebar, false); + const renderedCliSessionCount=_sessionSourceFilter==='cli' + ? sessions.length + : _countRenderedSidebarRows(allMatched, activeSidForSidebar, true); _syncSidebarExpansionForActiveSession(sessions, activeSidForSidebar); const list=$('sessionList'); const animateRefresh=_sessionListRefreshAnimationPending; @@ -4830,7 +4858,7 @@ function renderSessionListFromCache(){ const sourceTabs=document.createElement('div'); sourceTabs.className='session-source-tabs'; for(const filter of ['webui','cli']){ - const count=filter==='cli'?cliSessionCount:webuiSessionCount; + const count=filter==='cli'?renderedCliSessionCount:renderedWebuiSessionCount; const btn=document.createElement('button'); btn.type='button'; btn.className='session-source-tab'+(_sessionSourceFilter===filter?' active':''); diff --git a/tests/test_issue3966_session_count_label.py b/tests/test_issue3966_session_count_label.py new file mode 100644 index 00000000..e561d0ed --- /dev/null +++ b/tests/test_issue3966_session_count_label.py @@ -0,0 +1,32 @@ +"""Regression coverage for sidebar source counts using rendered rows.""" + +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SESSIONS_JS = (ROOT / "static" / "sessions.js").read_text(encoding="utf-8") + + +def test_render_counts_use_post_collapse_rows(): + render_start = SESSIONS_JS.index("function renderSessionListFromCache()") + render_end = SESSIONS_JS.index("function _showProjectPicker", render_start) + render_body = SESSIONS_JS[render_start:render_end] + + assert "const sessions=_renderSidebarRowsFromRawSessions(sessionsRaw);" in render_body + assert "? sessions.length" in render_body + assert ": _countRenderedSidebarRows(allMatched, activeSidForSidebar, false);" in render_body + assert ": _countRenderedSidebarRows(allMatched, activeSidForSidebar, true);" in render_body + assert "const count=filter==='cli'?renderedCliSessionCount:renderedWebuiSessionCount;" in render_body + assert "const count=filter==='cli'?cliSessionCount:webuiSessionCount;" not in render_body + + +def test_rendered_count_helper_collapses_before_counting(): + helper_start = SESSIONS_JS.index("function _countRenderedSidebarRows(") + helper_end = SESSIONS_JS.index("function renderSessionListFromCache()", helper_start) + helper_body = SESSIONS_JS[helper_start:helper_end] + + assert "sessionsRaw.push(s);" in helper_body + assert "return _renderSidebarRowsFromRawSessions(sessionsRaw).length;" in helper_body + assert "function _renderSidebarRowsFromRawSessions(sessionsRaw){" in SESSIONS_JS + assert "_attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw)" in SESSIONS_JS diff --git a/tests/test_sidebar_session_partition.py b/tests/test_sidebar_session_partition.py index c99f66b4..aa54b61f 100644 --- a/tests/test_sidebar_session_partition.py +++ b/tests/test_sidebar_session_partition.py @@ -20,6 +20,7 @@ def test_render_uses_single_pass_partition_helper(): render_body = SESSIONS_JS[render_start:render_end] assert "_partitionSidebarSessionRows(allMatched, activeSidForSidebar)" in render_body + assert "_renderSidebarRowsFromRawSessions(sessionsRaw)" in render_body assert "withMessages.filter(" not in render_body @@ -35,3 +36,14 @@ def test_partition_helper_applies_message_source_project_and_archive_gates(): assert "return {" in block assert "profileFiltered," in block assert "sessionsRaw," in block + + +def test_partition_helper_keeps_raw_source_counts_while_render_owns_visible_counts(): + render_start = SESSIONS_JS.index("function renderSessionListFromCache()") + render_end = SESSIONS_JS.index("function _showProjectPicker", render_start) + render_body = SESSIONS_JS[render_start:render_end] + + assert "webuiSessionCount," in _partition_block() + assert "cliSessionCount," in _partition_block() + assert "const renderedWebuiSessionCount=" in render_body + assert "const renderedCliSessionCount=" in render_body From 19448c326733bc94ec8a49dbdc6f1e9a1e142f58 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 12 Jun 2026 09:36:10 -0400 Subject: [PATCH 2/7] fix(sidebar): keep rendered source counts on one path --- static/sessions.js | 65 ++++++++++----------- tests/test_issue3966_session_count_label.py | 30 ++++++---- tests/test_sidebar_session_partition.py | 36 ++++++++---- 3 files changed, 74 insertions(+), 57 deletions(-) diff --git a/static/sessions.js b/static/sessions.js index 56c8d410..47739296 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -4730,39 +4730,45 @@ function _sidebarRowHasVisibleMessages(s, activeSidForSidebar){ function _partitionSidebarSessionRows(allMatched, activeSidForSidebar){ let webuiSessionCount=0; let cliSessionCount=0; - for(const s of allMatched){ - if(!_sidebarRowHasVisibleMessages(s, activeSidForSidebar)) continue; - if(_isCliSession(s)) cliSessionCount++; - else webuiSessionCount++; - } - if(_sessionSourceFilter==='cli' && !window._showCliSessions && cliSessionCount===0){ - _sessionSourceFilter='webui'; - } - const showCliOnly=_sessionSourceFilter==='cli'; - const profileFiltered=[]; - const sessionsRaw=[]; - let archivedCount=0; + const webuiProfileFiltered=[]; + const cliProfileFiltered=[]; + const webuiSessionsRaw=[]; + const cliSessionsRaw=[]; + let webuiArchivedCount=0; + let cliArchivedCount=0; for(const s of allMatched){ if(!_sidebarRowHasVisibleMessages(s, activeSidForSidebar)) continue; const isCli=_isCliSession(s); - if(showCliOnly ? !isCli : isCli) continue; + if(isCli) cliSessionCount++; + else webuiSessionCount++; if(s.default_hidden&&!(_activeProject&&_activeProject!==NO_PROJECT_FILTER&&s.project_id===_activeProject)) continue; + const profileFiltered=isCli ? cliProfileFiltered : webuiProfileFiltered; + const sessionsRaw=isCli ? cliSessionsRaw : webuiSessionsRaw; profileFiltered.push(s); if(_activeProject===NO_PROJECT_FILTER){ if(s.project_id) continue; } else if(_activeProject){ if(s.project_id!==_activeProject) continue; } - if(s.archived) archivedCount++; + if(s.archived){ + if(isCli) cliArchivedCount++; + else webuiArchivedCount++; + } if(!_showArchived&&s.archived) continue; sessionsRaw.push(s); } + if(_sessionSourceFilter==='cli' && !window._showCliSessions && cliSessionCount===0){ + _sessionSourceFilter='webui'; + } + const showCliOnly=_sessionSourceFilter==='cli'; return { webuiSessionCount, cliSessionCount, - profileFiltered, - sessionsRaw, - archivedCount, + profileFiltered: showCliOnly ? cliProfileFiltered : webuiProfileFiltered, + sessionsRaw: showCliOnly ? cliSessionsRaw : webuiSessionsRaw, + archivedCount: showCliOnly ? cliArchivedCount : webuiArchivedCount, + webuiSessionsRaw, + cliSessionsRaw, }; } @@ -4770,22 +4776,9 @@ function _renderSidebarRowsFromRawSessions(sessionsRaw){ return _attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw); } -function _countRenderedSidebarRows(allMatched, activeSidForSidebar, showCliOnly){ - const sessionsRaw=[]; - for(const s of allMatched){ - if(!_sidebarRowHasVisibleMessages(s, activeSidForSidebar)) continue; - const isCli=_isCliSession(s); - if(showCliOnly ? !isCli : isCli) continue; - if(s.default_hidden&&!(_activeProject&&_activeProject!==NO_PROJECT_FILTER&&s.project_id===_activeProject)) continue; - if(_activeProject===NO_PROJECT_FILTER){ - if(s.project_id) continue; - } else if(_activeProject){ - if(s.project_id!==_activeProject) continue; - } - if(!_showArchived&&s.archived) continue; - sessionsRaw.push(s); - } - return _renderSidebarRowsFromRawSessions(sessionsRaw).length; +function _countRenderedSidebarRowsFromRawSessions(sessionsRaw){ + // Child attachment does not change the top-level row count shown in the chip label. + return _collapseSessionLineageForSidebar(sessionsRaw).length; } function renderSessionListFromCache(){ @@ -4816,14 +4809,16 @@ function renderSessionListFromCache(){ profileFiltered, sessionsRaw, archivedCount, + webuiSessionsRaw, + cliSessionsRaw, }=_partitionSidebarSessionRows(allMatched, activeSidForSidebar); const sessions=_renderSidebarRowsFromRawSessions(sessionsRaw); const renderedWebuiSessionCount=_sessionSourceFilter==='webui' ? sessions.length - : _countRenderedSidebarRows(allMatched, activeSidForSidebar, false); + : _countRenderedSidebarRowsFromRawSessions(webuiSessionsRaw); const renderedCliSessionCount=_sessionSourceFilter==='cli' ? sessions.length - : _countRenderedSidebarRows(allMatched, activeSidForSidebar, true); + : _countRenderedSidebarRowsFromRawSessions(cliSessionsRaw); _syncSidebarExpansionForActiveSession(sessions, activeSidForSidebar); const list=$('sessionList'); const animateRefresh=_sessionListRefreshAnimationPending; diff --git a/tests/test_issue3966_session_count_label.py b/tests/test_issue3966_session_count_label.py index e561d0ed..4d99f1e8 100644 --- a/tests/test_issue3966_session_count_label.py +++ b/tests/test_issue3966_session_count_label.py @@ -8,25 +8,35 @@ ROOT = Path(__file__).resolve().parents[1] SESSIONS_JS = (ROOT / "static" / "sessions.js").read_text(encoding="utf-8") +def _function_block(name: str) -> str: + start = SESSIONS_JS.index(f"function {name}(") + brace = SESSIONS_JS.index("{", start) + depth = 0 + for idx in range(brace, len(SESSIONS_JS)): + char = SESSIONS_JS[idx] + if char == "{": + depth += 1 + elif char == "}": + depth -= 1 + if depth == 0: + return SESSIONS_JS[start : idx + 1] + raise AssertionError(f"unbalanced braces in {name}") + + def test_render_counts_use_post_collapse_rows(): - render_start = SESSIONS_JS.index("function renderSessionListFromCache()") - render_end = SESSIONS_JS.index("function _showProjectPicker", render_start) - render_body = SESSIONS_JS[render_start:render_end] + render_body = _function_block("renderSessionListFromCache") assert "const sessions=_renderSidebarRowsFromRawSessions(sessionsRaw);" in render_body assert "? sessions.length" in render_body - assert ": _countRenderedSidebarRows(allMatched, activeSidForSidebar, false);" in render_body - assert ": _countRenderedSidebarRows(allMatched, activeSidForSidebar, true);" in render_body + assert ": _countRenderedSidebarRowsFromRawSessions(webuiSessionsRaw);" in render_body + assert ": _countRenderedSidebarRowsFromRawSessions(cliSessionsRaw);" in render_body assert "const count=filter==='cli'?renderedCliSessionCount:renderedWebuiSessionCount;" in render_body assert "const count=filter==='cli'?cliSessionCount:webuiSessionCount;" not in render_body def test_rendered_count_helper_collapses_before_counting(): - helper_start = SESSIONS_JS.index("function _countRenderedSidebarRows(") - helper_end = SESSIONS_JS.index("function renderSessionListFromCache()", helper_start) - helper_body = SESSIONS_JS[helper_start:helper_end] + helper_body = _function_block("_countRenderedSidebarRowsFromRawSessions") - assert "sessionsRaw.push(s);" in helper_body - assert "return _renderSidebarRowsFromRawSessions(sessionsRaw).length;" in helper_body + assert "_collapseSessionLineageForSidebar(sessionsRaw).length;" in helper_body assert "function _renderSidebarRowsFromRawSessions(sessionsRaw){" in SESSIONS_JS assert "_attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw)" in SESSIONS_JS diff --git a/tests/test_sidebar_session_partition.py b/tests/test_sidebar_session_partition.py index aa54b61f..8d5bdda7 100644 --- a/tests/test_sidebar_session_partition.py +++ b/tests/test_sidebar_session_partition.py @@ -8,16 +8,27 @@ ROOT = Path(__file__).resolve().parents[1] SESSIONS_JS = (ROOT / "static" / "sessions.js").read_text(encoding="utf-8") +def _function_block(name: str) -> str: + start = SESSIONS_JS.index(f"function {name}(") + brace = SESSIONS_JS.index("{", start) + depth = 0 + for idx in range(brace, len(SESSIONS_JS)): + char = SESSIONS_JS[idx] + if char == "{": + depth += 1 + elif char == "}": + depth -= 1 + if depth == 0: + return SESSIONS_JS[start : idx + 1] + raise AssertionError(f"unbalanced braces in {name}") + + def _partition_block() -> str: - start = SESSIONS_JS.index("function _partitionSidebarSessionRows(") - end = SESSIONS_JS.index("function renderSessionListFromCache()", start) - return SESSIONS_JS[start:end] + return _function_block("_partitionSidebarSessionRows") def test_render_uses_single_pass_partition_helper(): - render_start = SESSIONS_JS.index("function renderSessionListFromCache()") - render_end = SESSIONS_JS.index("function _showProjectPicker", render_start) - render_body = SESSIONS_JS[render_start:render_end] + render_body = _function_block("renderSessionListFromCache") assert "_partitionSidebarSessionRows(allMatched, activeSidForSidebar)" in render_body assert "_renderSidebarRowsFromRawSessions(sessionsRaw)" in render_body @@ -32,18 +43,19 @@ def test_partition_helper_applies_message_source_project_and_archive_gates(): assert "if(_sessionSourceFilter==='cli' && !window._showCliSessions && cliSessionCount===0)" in block assert "const showCliOnly=_sessionSourceFilter==='cli';" in block assert "if(!_showArchived&&s.archived) continue;" in block - assert "if(s.archived) archivedCount++;" in block + assert "if(s.archived){" in block + assert "archivedCount: showCliOnly ? cliArchivedCount : webuiArchivedCount," in block assert "return {" in block - assert "profileFiltered," in block - assert "sessionsRaw," in block + assert "profileFiltered: showCliOnly ? cliProfileFiltered : webuiProfileFiltered," in block + assert "sessionsRaw: showCliOnly ? cliSessionsRaw : webuiSessionsRaw," in block def test_partition_helper_keeps_raw_source_counts_while_render_owns_visible_counts(): - render_start = SESSIONS_JS.index("function renderSessionListFromCache()") - render_end = SESSIONS_JS.index("function _showProjectPicker", render_start) - render_body = SESSIONS_JS[render_start:render_end] + render_body = _function_block("renderSessionListFromCache") assert "webuiSessionCount," in _partition_block() assert "cliSessionCount," in _partition_block() + assert "webuiSessionsRaw," in _partition_block() + assert "cliSessionsRaw," in _partition_block() assert "const renderedWebuiSessionCount=" in render_body assert "const renderedCliSessionCount=" in render_body From dde9f06b0330e3c0c017e6119a5cd34f8a9a3838 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 12 Jun 2026 09:52:49 -0400 Subject: [PATCH 3/7] test(sidebar): avoid shard churn from static regressions --- static/sessions.js | 1 - tests/test_issue3966_session_count_label.py | 42 --------------------- tests/test_sidebar_session_partition.py | 9 +++++ 3 files changed, 9 insertions(+), 43 deletions(-) delete mode 100644 tests/test_issue3966_session_count_label.py diff --git a/static/sessions.js b/static/sessions.js index 47739296..55b7674b 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -4804,7 +4804,6 @@ function renderSessionListFromCache(){ const searchMatches=_sessionSearchMergeMatches(sidebarRows,searchQueryRaw,_contentSearchResults); const allMatched=_ensureActiveSessionRowPresent(searchMatches,sidebarRows); const { - webuiSessionCount, cliSessionCount, profileFiltered, sessionsRaw, diff --git a/tests/test_issue3966_session_count_label.py b/tests/test_issue3966_session_count_label.py deleted file mode 100644 index 4d99f1e8..00000000 --- a/tests/test_issue3966_session_count_label.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Regression coverage for sidebar source counts using rendered rows.""" - -from __future__ import annotations - -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] -SESSIONS_JS = (ROOT / "static" / "sessions.js").read_text(encoding="utf-8") - - -def _function_block(name: str) -> str: - start = SESSIONS_JS.index(f"function {name}(") - brace = SESSIONS_JS.index("{", start) - depth = 0 - for idx in range(brace, len(SESSIONS_JS)): - char = SESSIONS_JS[idx] - if char == "{": - depth += 1 - elif char == "}": - depth -= 1 - if depth == 0: - return SESSIONS_JS[start : idx + 1] - raise AssertionError(f"unbalanced braces in {name}") - - -def test_render_counts_use_post_collapse_rows(): - render_body = _function_block("renderSessionListFromCache") - - assert "const sessions=_renderSidebarRowsFromRawSessions(sessionsRaw);" in render_body - assert "? sessions.length" in render_body - assert ": _countRenderedSidebarRowsFromRawSessions(webuiSessionsRaw);" in render_body - assert ": _countRenderedSidebarRowsFromRawSessions(cliSessionsRaw);" in render_body - assert "const count=filter==='cli'?renderedCliSessionCount:renderedWebuiSessionCount;" in render_body - assert "const count=filter==='cli'?cliSessionCount:webuiSessionCount;" not in render_body - - -def test_rendered_count_helper_collapses_before_counting(): - helper_body = _function_block("_countRenderedSidebarRowsFromRawSessions") - - assert "_collapseSessionLineageForSidebar(sessionsRaw).length;" in helper_body - assert "function _renderSidebarRowsFromRawSessions(sessionsRaw){" in SESSIONS_JS - assert "_attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw)" in SESSIONS_JS diff --git a/tests/test_sidebar_session_partition.py b/tests/test_sidebar_session_partition.py index 8d5bdda7..90f4d8fb 100644 --- a/tests/test_sidebar_session_partition.py +++ b/tests/test_sidebar_session_partition.py @@ -32,6 +32,11 @@ def test_render_uses_single_pass_partition_helper(): assert "_partitionSidebarSessionRows(allMatched, activeSidForSidebar)" in render_body assert "_renderSidebarRowsFromRawSessions(sessionsRaw)" in render_body + assert "? sessions.length" in render_body + assert ": _countRenderedSidebarRowsFromRawSessions(webuiSessionsRaw);" in render_body + assert ": _countRenderedSidebarRowsFromRawSessions(cliSessionsRaw);" in render_body + assert "const count=filter==='cli'?renderedCliSessionCount:renderedWebuiSessionCount;" in render_body + assert "const count=filter==='cli'?cliSessionCount:webuiSessionCount;" not in render_body assert "withMessages.filter(" not in render_body @@ -59,3 +64,7 @@ def test_partition_helper_keeps_raw_source_counts_while_render_owns_visible_coun assert "cliSessionsRaw," in _partition_block() assert "const renderedWebuiSessionCount=" in render_body assert "const renderedCliSessionCount=" in render_body + helper_body = _function_block("_countRenderedSidebarRowsFromRawSessions") + assert "_collapseSessionLineageForSidebar(sessionsRaw).length;" in helper_body + assert "function _renderSidebarRowsFromRawSessions(sessionsRaw){" in SESSIONS_JS + assert "_attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw)" in SESSIONS_JS From 66a2d577df4b0d633c9fc3fa6b2cfcb59fd82c8f Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 12 Jun 2026 10:05:33 -0400 Subject: [PATCH 4/7] Keep CLI sidebar regression aligned with bucketed partitioning --- tests/test_issue2351_cli_session_source_filter.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_issue2351_cli_session_source_filter.py b/tests/test_issue2351_cli_session_source_filter.py index e6ba76c2..38678eda 100644 --- a/tests/test_issue2351_cli_session_source_filter.py +++ b/tests/test_issue2351_cli_session_source_filter.py @@ -22,7 +22,10 @@ def test_cli_filter_keeps_cli_rows_out_of_default_webui_list(): assert "webuiSessionCount" in src assert "cliSessionCount" in src assert "const showCliOnly=_sessionSourceFilter==='cli';" in src - assert "if(showCliOnly ? !isCli : isCli) continue;" in src + assert "const webuiProfileFiltered=[];" in src + assert "const cliProfileFiltered=[];" in src + assert "profileFiltered: showCliOnly ? cliProfileFiltered : webuiProfileFiltered," in src + assert "sessionsRaw: showCliOnly ? cliSessionsRaw : webuiSessionsRaw," in src def test_session_source_tabs_have_dedicated_sidebar_styles(): From 0ed902bd05291f4ed12cf52d4138c540244ac327 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 12 Jun 2026 11:55:43 -0400 Subject: [PATCH 5/7] fix(sidebar): drop the dead WebUI count path --- static/sessions.js | 3 --- tests/test_issue2351_cli_session_source_filter.py | 3 ++- tests/test_sidebar_session_partition.py | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/static/sessions.js b/static/sessions.js index 55b7674b..1eb9779e 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -4728,7 +4728,6 @@ function _sidebarRowHasVisibleMessages(s, activeSidForSidebar){ } function _partitionSidebarSessionRows(allMatched, activeSidForSidebar){ - let webuiSessionCount=0; let cliSessionCount=0; const webuiProfileFiltered=[]; const cliProfileFiltered=[]; @@ -4740,7 +4739,6 @@ function _partitionSidebarSessionRows(allMatched, activeSidForSidebar){ if(!_sidebarRowHasVisibleMessages(s, activeSidForSidebar)) continue; const isCli=_isCliSession(s); if(isCli) cliSessionCount++; - else webuiSessionCount++; if(s.default_hidden&&!(_activeProject&&_activeProject!==NO_PROJECT_FILTER&&s.project_id===_activeProject)) continue; const profileFiltered=isCli ? cliProfileFiltered : webuiProfileFiltered; const sessionsRaw=isCli ? cliSessionsRaw : webuiSessionsRaw; @@ -4762,7 +4760,6 @@ function _partitionSidebarSessionRows(allMatched, activeSidForSidebar){ } const showCliOnly=_sessionSourceFilter==='cli'; return { - webuiSessionCount, cliSessionCount, profileFiltered: showCliOnly ? cliProfileFiltered : webuiProfileFiltered, sessionsRaw: showCliOnly ? cliSessionsRaw : webuiSessionsRaw, diff --git a/tests/test_issue2351_cli_session_source_filter.py b/tests/test_issue2351_cli_session_source_filter.py index 38678eda..71312b69 100644 --- a/tests/test_issue2351_cli_session_source_filter.py +++ b/tests/test_issue2351_cli_session_source_filter.py @@ -19,11 +19,12 @@ def test_sidebar_has_separate_webui_and_cli_session_source_tabs(): def test_cli_filter_keeps_cli_rows_out_of_default_webui_list(): src = SESSIONS_JS.read_text(encoding="utf-8") assert "function _partitionSidebarSessionRows(allMatched, activeSidForSidebar)" in src - assert "webuiSessionCount" in src assert "cliSessionCount" in src assert "const showCliOnly=_sessionSourceFilter==='cli';" in src assert "const webuiProfileFiltered=[];" in src assert "const cliProfileFiltered=[];" in src + assert "const webuiSessionsRaw=[];" in src + assert "const cliSessionsRaw=[];" in src assert "profileFiltered: showCliOnly ? cliProfileFiltered : webuiProfileFiltered," in src assert "sessionsRaw: showCliOnly ? cliSessionsRaw : webuiSessionsRaw," in src diff --git a/tests/test_sidebar_session_partition.py b/tests/test_sidebar_session_partition.py index 90f4d8fb..8d3f3f5a 100644 --- a/tests/test_sidebar_session_partition.py +++ b/tests/test_sidebar_session_partition.py @@ -58,7 +58,7 @@ def test_partition_helper_applies_message_source_project_and_archive_gates(): def test_partition_helper_keeps_raw_source_counts_while_render_owns_visible_counts(): render_body = _function_block("renderSessionListFromCache") - assert "webuiSessionCount," in _partition_block() + assert "webuiSessionCount," not in _partition_block() assert "cliSessionCount," in _partition_block() assert "webuiSessionsRaw," in _partition_block() assert "cliSessionsRaw," in _partition_block() From 6a1109475bceb436ba6902ab4a38d8aed478bf0c Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 12 Jun 2026 12:03:04 -0400 Subject: [PATCH 6/7] fix(sidebar): count inactive rows through the render path --- static/sessions.js | 4 ++-- tests/test_sidebar_session_partition.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/sessions.js b/static/sessions.js index 1eb9779e..355f8557 100644 --- a/static/sessions.js +++ b/static/sessions.js @@ -4774,8 +4774,8 @@ function _renderSidebarRowsFromRawSessions(sessionsRaw){ } function _countRenderedSidebarRowsFromRawSessions(sessionsRaw){ - // Child attachment does not change the top-level row count shown in the chip label. - return _collapseSessionLineageForSidebar(sessionsRaw).length; + // Keep inactive-tab chip counts on the exact same top-level row path as render. + return _renderSidebarRowsFromRawSessions(sessionsRaw).length; } function renderSessionListFromCache(){ diff --git a/tests/test_sidebar_session_partition.py b/tests/test_sidebar_session_partition.py index 8d3f3f5a..cc45cda1 100644 --- a/tests/test_sidebar_session_partition.py +++ b/tests/test_sidebar_session_partition.py @@ -65,6 +65,6 @@ def test_partition_helper_keeps_raw_source_counts_while_render_owns_visible_coun assert "const renderedWebuiSessionCount=" in render_body assert "const renderedCliSessionCount=" in render_body helper_body = _function_block("_countRenderedSidebarRowsFromRawSessions") - assert "_collapseSessionLineageForSidebar(sessionsRaw).length;" in helper_body + assert "_renderSidebarRowsFromRawSessions(sessionsRaw).length;" in helper_body assert "function _renderSidebarRowsFromRawSessions(sessionsRaw){" in SESSIONS_JS assert "_attachChildSessionsToSidebarRows(_collapseSessionLineageForSidebar(sessionsRaw), sessionsRaw)" in SESSIONS_JS From db2d6eb97ae1016a23594b8cf3c602b33b005c4d Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Fri, 12 Jun 2026 16:53:03 -0400 Subject: [PATCH 7/7] test(pytest): suppress shard nodeid floods in verbose CI --- tests/conftest.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 6e51fdfb..96643486 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -238,6 +238,19 @@ def pytest_configure(config): config.addinivalue_line("markers", "requires_agent_modules: skip when hermes-agent Python modules are not importable") +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_report_collectionfinish(config, items): + """Avoid pytest-shard's giant nodeid dump on sharded `-v` CI runs.""" + verbose = getattr(config.option, "verbose", 0) + shard_total = getattr(config.option, "num_shards", 1) + if shard_total > 1 and verbose > 0: + config.option.verbose = 0 + try: + yield + finally: + config.option.verbose = verbose + + # ── Disable AWS IMDS probing for the pytest session ──────────────────────── # Background: when hermes-agent's bedrock_adapter / botocore credential chain # runs during test execution (e.g. provider catalog enumeration triggered by