fix(sidebar): Unassigned chip resolves child projects through reference rows

The chip check now reuses the ancestor map built by _partitionSidebarSessionRows,
so a child whose project parent is only a hidden reference row is not counted
as unassigned. Document project inheritance for subagent sessions in README.
This commit is contained in:
carlotestor committed 2026-09-23 12:59:36 +00:00
1 parent d04737ec87
commit 35c9b8ae96
4 files changed
+26 -6

No files matched your search

+1 -1
View File
@@ -220,7 +220,7 @@ If an AI assistant is helping with install, reinstall, bootstrap, provider setup
- Session actions via `⋯` dropdown per session — pin, move to project, archive, duplicate, delete
- Pin/star sessions to the top of the sidebar (gold indicator)
- Archive sessions (hide without deleting, toggle to show)
- Session projects -- named groups with colors for organizing sessions
- Session projects -- named groups with colors for organizing sessions; delegated subagent sessions have no project of their own and follow their nearest ancestor's project in the project filter and the Unassigned chip
- Session tags -- add #tag to titles for colored chips and click-to-filter
- Grouped by Today / Yesterday / Earlier in the sidebar (collapsible date groups)
- Download as Markdown transcript, full JSON export, or import from JSON
+8 -2
View File
@@ -7622,6 +7622,11 @@ function _sidebarRowsById(rows){
return byId;
}
// True when any row resolves to no project, using the filter's own ancestor map.
function _sidebarHasUnprojectedRows(rows, rowsById){
return rows.some(s=>!_sidebarProjectIdForRow(s, rowsById));
}
function _partitionSidebarSessionRows(allMatched, activeSidForSidebar){
const rowsById=_sidebarRowsById([allMatched, typeof _sidebarReferenceSessions!=='undefined'?_sidebarReferenceSessions:null]);
let cliSessionCount=0;
@@ -7670,6 +7675,7 @@ function _partitionSidebarSessionRows(allMatched, activeSidForSidebar){
cliReferenceRaw,
webuiSessionsRaw,
cliSessionsRaw,
rowsById,
};
}
@@ -7797,6 +7803,7 @@ function renderSessionListFromCache(){
cliReferenceRaw,
webuiSessionsRaw,
cliSessionsRaw,
rowsById,
}=_partitionSidebarSessionRows(allMatched, activeSidForSidebar);
const referenceRaw=_sessionSourceFilter==='cli'?cliReferenceRaw:webuiReferenceRaw;
const isCliView=_sessionSourceFilter==='cli';
@@ -7871,8 +7878,7 @@ function renderSessionListFromCache(){
}
// Project filter bar — show when there are real projects OR there are
// unassigned sessions (so the Unassigned chip has something to filter to).
const profileRowsById=_sidebarRowsById([profileFiltered]);
const hasUnprojected=profileFiltered.some(s=>!_sidebarProjectIdForRow(s, profileRowsById));
const hasUnprojected=_sidebarHasUnprojectedRows(profileFiltered, rowsById);
if(_allProjects.length>0||hasUnprojected){
const bar=document.createElement('div');
bar.className='project-bar';
+1 -1
View File
@@ -76,7 +76,7 @@ def test_unassigned_chip_only_shown_when_relevant():
— though that's vanishingly rare).
"""
js = _js()
assert "const hasUnprojected=profileFiltered.some(s=>!_sidebarProjectIdForRow(s, profileRowsById));" in js, (
assert "const hasUnprojected=_sidebarHasUnprojectedRows(profileFiltered, rowsById);" in js, (
"The render function must compute whether unassigned sessions exist"
)
assert "if(_allProjects.length>0||hasUnprojected){" in js, (
+16 -2
View File
@@ -44,7 +44,7 @@ global._sessionSourceFilter = 'webui';
for (const fn of ['_isSessionLocallyStreaming','_hasPendingUserMessageSignal','_isSessionEffectivelyStreaming',
'_isChildSession','_isForkWithResolvableParent','_sessionLineageKey','_sidebarLineageKeyForRow',
'_collapseSessionLineageForSidebar','_attachChildSessionsToSidebarRows','_sessionAttentionState',
'_sidebarRowHasVisibleMessages','_sidebarProjectIdForRow','_sidebarRowsById',
'_sidebarRowHasVisibleMessages','_sidebarProjectIdForRow','_sidebarRowsById','_sidebarHasUnprojectedRows',
'_partitionSidebarSessionRows','_scopedSidebarReferenceRows','_renderSidebarRowsFromRawSessions']) {
eval.call(global, extractFunc(fn));
}
@@ -115,4 +115,18 @@ console.log(JSON.stringify({raw: rows.raw, scoped}));
def test_unassigned_chip_ignores_subagents_of_project_parents():
assert "const hasUnprojected=profileFiltered.some(s=>!_sidebarProjectIdForRow(s, profileRowsById));" in SESSIONS_JS
out = _run("console.log(JSON.stringify(_sidebarHasUnprojectedRows([parent, child], _sidebarRowsById([[parent, child]]))));")
assert out is False
def test_unassigned_chip_uses_reference_parent():
"""A child whose project parent is only a reference row is not unassigned."""
out = _run("""
global._sidebarReferenceSessions = [parent];
global._activeProject = null;
const part = _partitionSidebarSessionRows([child], null);
console.log(JSON.stringify({has: _sidebarHasUnprojectedRows(part.profileFiltered, part.rowsById),
unassigned: render(NO_PROJECT_FILTER, [child]).raw}));
""")
assert out["unassigned"] == []
assert out["has"] is False