diff --git a/apps/desktop/src/app/chat/sidebar/projects/workspace-groups.test.ts b/apps/desktop/src/app/chat/sidebar/projects/workspace-groups.test.ts index 213bff09ba5e4..9c1711b192a82 100644 --- a/apps/desktop/src/app/chat/sidebar/projects/workspace-groups.test.ts +++ b/apps/desktop/src/app/chat/sidebar/projects/workspace-groups.test.ts @@ -471,6 +471,15 @@ describe('liveSessionProjectId', () => { expect(id).toBe('p_app') }) + it('anchors a cwd-less session on its git_repo_root (backend groups it there too)', () => { + // Older/imported rows carry only a repo root; the sidebar files them under + // the repo's project, so membership (and color) must resolve from the root. + expect(liveSessionProjectId(makeSession(null, { git_repo_root: '/www/app' }), [])).toBe('/www/app') + expect( + liveSessionProjectId(makeSession(null, { git_repo_root: '/www/app' }), [makeProject('p_app', ['/www/app'])]) + ).toBe('p_app') + }) + it('skips cwd-less, kanban-task, and out-of-tree (sibling) worktree sessions', () => { expect(liveSessionProjectId(makeSession(null), [])).toBeNull() // Kanban task worktree → folds into the kanban bucket, not a project preview. @@ -538,6 +547,12 @@ describe('sessionProjectColor', () => { expect(sessionProjectColor(session, [makeProject('p_app', ['/www/app'])])).toBeNull() }) + it('colors a cwd-less session by its git_repo_root project (the grouped-but-grey fix)', () => { + const session = makeSession(null, { git_repo_root: '/www/app' }) + + expect(sessionProjectColor(session, [colored('p_app', ['/www/app'], '#4a9eff')])).toBe('#4a9eff') + }) + it('returns null for a session that only maps to an auto repo root (no explicit project)', () => { // liveSessionProjectId falls back to the repo root id, which is not a // project row and therefore carries no color. diff --git a/apps/desktop/src/app/chat/sidebar/projects/workspace-groups.ts b/apps/desktop/src/app/chat/sidebar/projects/workspace-groups.ts index afeb230c59fa8..7931030feb111 100644 --- a/apps/desktop/src/app/chat/sidebar/projects/workspace-groups.ts +++ b/apps/desktop/src/app/chat/sidebar/projects/workspace-groups.ts @@ -361,15 +361,21 @@ function isPathUnder(folder: string, target: string): boolean { */ export function liveSessionProjectId(session: SessionInfo, explicitProjects: ProjectInfo[]): null | string { const cwd = (session.cwd || '').trim() + // A session may carry only a git_repo_root and no cwd — older/imported rows, + // or ones captured before cwd tracking. The backend still groups those by repo + // root, so anchor on it here too; otherwise the sidebar files the row under a + // project but the color derivation drops it (the "grouped but grey" bug). + const repoRoot = (session.git_repo_root || '').trim() || cwd + const anchor = cwd || repoRoot - if (!cwd || kanbanWorktreeDir(cwd)) { + if (!anchor || kanbanWorktreeDir(anchor)) { return null } - // No persisted repo root yet (brand-new session) → the cwd is the root. - const repoRoot = (session.git_repo_root || '').trim() || cwd - - if (!isPathUnder(repoRoot, cwd)) { + // With a cwd present it must sit under the repo root (a sibling worktree + // outside the root can't be placed from the row alone); a root-only session + // skips this — the root IS the anchor. + if (cwd && !isPathUnder(repoRoot, cwd)) { return null }