From 99a599e6f450edf26b61c1f6eda97b37d8ad9c8c Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Sun, 19 Jul 2026 07:32:36 -0400 Subject: [PATCH] fix(desktop): resolve session color for repo-root-only sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit liveSessionProjectId bailed the instant a session had no cwd, so an older/imported session carrying only a git_repo_root — which the backend still groups under its project — got no project and rendered a grey idle dot instead of the project color ("grouped but grey"). Anchor on the repo root when cwd is absent, matching how the sidebar grouped the row, and keep the sibling-worktree guard for the cwd-present case. --- .../sidebar/projects/workspace-groups.test.ts | 15 +++++++++++++++ .../chat/sidebar/projects/workspace-groups.ts | 16 +++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) 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 }