diff --git a/desktop/src/components/layout/Sidebar.test.tsx b/desktop/src/components/layout/Sidebar.test.tsx
index 62960d1e..f80b585a 100644
--- a/desktop/src/components/layout/Sidebar.test.tsx
+++ b/desktop/src/components/layout/Sidebar.test.tsx
@@ -799,6 +799,33 @@ describe('Sidebar', () => {
expect(screen.getByRole('button', { name: /Drive Project Session/ })).toBeInTheDocument()
})
+ it('does not restore a hidden Windows drive root when creating a child project session', async () => {
+ window.localStorage.setItem(PROJECT_HIDDEN_STORAGE_KEY, JSON.stringify(['D:\\']))
+ createSession.mockResolvedValue('child-new')
+ const now = new Date().toISOString()
+ useSessionStore.setState({
+ sessions: [
+ makeSession('child-1', 'Child Session', 'D:\\workspace\\code\\cc-haha', now),
+ ],
+ })
+ useTabStore.setState({
+ tabs: [{ sessionId: 'child-1', title: 'Child Session', type: 'session', status: 'idle' }],
+ activeTabId: 'child-1',
+ })
+
+ render()
+
+ await act(async () => {
+ fireEvent.click(screen.getByRole('button', { name: 'New Session' }))
+ })
+
+ await waitFor(() => {
+ expect(createSession).toHaveBeenCalledWith('D:\\workspace\\code\\cc-haha')
+ })
+ expect(JSON.parse(window.localStorage.getItem(PROJECT_HIDDEN_STORAGE_KEY) ?? '[]')).toEqual(['D:\\'])
+ expect(desktopUiPreferencesApiMock.updateSidebarPreferences).not.toHaveBeenCalled()
+ })
+
it('right-aligns running status, worktree marker, and update time on session rows', () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2026-05-19T12:00:00.000Z'))
diff --git a/desktop/src/components/layout/Sidebar.tsx b/desktop/src/components/layout/Sidebar.tsx
index 9860caeb..9e836484 100644
--- a/desktop/src/components/layout/Sidebar.tsx
+++ b/desktop/src/components/layout/Sidebar.tsx
@@ -1657,17 +1657,21 @@ function normalizeProjectKeyList(values: unknown): string[] {
}
function normalizeProjectPathForComparison(value: string): string {
- const normalized = value.replace(/\\/g, '/').replace(/\/+$/g, '')
- return normalized || value
+ const normalized = value.replace(/\\/g, '/').replace(/\/+$/g, '') || value
+ return isWindows ? normalized.toLowerCase() : normalized
+}
+
+function isDriveRootComparisonPath(value: string): boolean {
+ return /^[a-z]:$/i.test(value)
}
function projectPathMatches(projectKey: string, workDir: string): boolean {
const normalizedProjectKey = normalizeProjectPathForComparison(projectKey)
const normalizedWorkDir = normalizeProjectPathForComparison(workDir)
- return normalizedProjectKey === normalizedWorkDir
- || normalizedWorkDir.startsWith(`${normalizedProjectKey}/`)
- || normalizedProjectKey.startsWith(`${normalizedWorkDir}/`)
+ if (normalizedProjectKey === normalizedWorkDir) return true
+ if (isDriveRootComparisonPath(normalizedProjectKey)) return false
+ return normalizedWorkDir.startsWith(`${normalizedProjectKey}/`)
}
function hasSidebarProjectPreferences(preferences: SidebarProjectPreferences): boolean {
diff --git a/src/server/__tests__/windows-drive-path.test.ts b/src/server/__tests__/windows-drive-path.test.ts
index b48a9c7c..6d5c3fa0 100644
--- a/src/server/__tests__/windows-drive-path.test.ts
+++ b/src/server/__tests__/windows-drive-path.test.ts
@@ -1,9 +1,14 @@
import { describe, expect, it } from 'bun:test'
+import * as fs from 'node:fs/promises'
+import * as os from 'node:os'
+import * as path from 'node:path'
import {
isSameOrInsidePathForPlatform,
normalizeDriveRootPathForPlatform,
} from '../services/windowsDrivePath.js'
+import { getRepositoryContext } from '../services/repositoryLaunchService.js'
import { SessionService } from '../services/sessionService.js'
+import { sanitizePath } from '../../utils/sessionStoragePortable.js'
describe('Windows drive root path handling', () => {
it('normalizes drive-relative root inputs to absolute drive roots on Windows', () => {
@@ -27,4 +32,46 @@ describe('Windows drive root path handling', () => {
expect(isSameOrInsidePathForPlatform('D:\\project-extra', 'D:\\project', 'win32')).toBe(false)
expect(isSameOrInsidePathForPlatform('E:\\child', 'D:\\', 'win32')).toBe(false)
})
+
+ it('keeps realpathed drive roots from resolving to the current drive directory', async () => {
+ if (process.platform !== 'win32') return
+
+ const originalConfigDir = process.env.CLAUDE_CONFIG_DIR
+ const configDir = await fs.mkdtemp(path.join(os.tmpdir(), 'drive-root-session-'))
+ const driveRoot = path.parse(process.cwd()).root
+ const sessionId = crypto.randomUUID()
+
+ try {
+ process.env.CLAUDE_CONFIG_DIR = configDir
+ const projectDir = path.join(configDir, 'projects', sanitizePath(driveRoot))
+ await fs.mkdir(projectDir, { recursive: true })
+ await fs.writeFile(
+ path.join(projectDir, `${sessionId}.jsonl`),
+ JSON.stringify({
+ type: 'session-meta',
+ isMeta: true,
+ workDir: driveRoot,
+ timestamp: new Date().toISOString(),
+ }) + '\n',
+ 'utf-8',
+ )
+
+ const service = new SessionService()
+ const { sessions } = await service.listSessions({ limit: 5 })
+ const session = sessions.find((item) => item.id === sessionId)
+ expect(session?.workDir).toBe(driveRoot)
+ expect(session?.projectRoot).toBe(driveRoot)
+
+ const context = await getRepositoryContext(driveRoot)
+ expect(context.workDir).toBe(driveRoot)
+ expect(context.repoRoot).not.toBe(process.cwd())
+ } finally {
+ if (originalConfigDir === undefined) {
+ delete process.env.CLAUDE_CONFIG_DIR
+ } else {
+ process.env.CLAUDE_CONFIG_DIR = originalConfigDir
+ }
+ await fs.rm(configDir, { recursive: true, force: true })
+ }
+ })
})
diff --git a/src/server/services/repositoryLaunchService.ts b/src/server/services/repositoryLaunchService.ts
index b2487732..aa4c6590 100644
--- a/src/server/services/repositoryLaunchService.ts
+++ b/src/server/services/repositoryLaunchService.ts
@@ -172,7 +172,7 @@ async function resolveDirectory(workDir: string): Promise {
const resolved = path.resolve(normalizeDriveRootPathForPlatform(workDir))
let realPath: string
try {
- realPath = await fs.realpath(resolved)
+ realPath = normalizeDriveRootPathForPlatform(await fs.realpath(resolved))
} catch {
throw repositoryBadRequest(
REPOSITORY_ERROR.workdirMissing,
diff --git a/src/server/services/sessionService.ts b/src/server/services/sessionService.ts
index 03e46a28..50dfdd97 100644
--- a/src/server/services/sessionService.ts
+++ b/src/server/services/sessionService.ts
@@ -366,7 +366,7 @@ export class SessionService {
private async canonicalizeProjectPath(projectPath: string): Promise {
try {
- return (await fs.realpath(projectPath)).normalize('NFC')
+ return normalizeDriveRootPathForPlatform(await fs.realpath(projectPath)).normalize('NFC')
} catch {
return projectPath.normalize('NFC')
}
diff --git a/src/server/services/workspaceService.ts b/src/server/services/workspaceService.ts
index b9ba02cb..196bd14f 100644
--- a/src/server/services/workspaceService.ts
+++ b/src/server/services/workspaceService.ts
@@ -1030,7 +1030,7 @@ export class WorkspaceService {
return {
kind: 'ok',
workspaceRoot: workDir,
- canonicalWorkspaceRoot: await fs.realpath(workDir),
+ canonicalWorkspaceRoot: normalizeDriveRootPathForPlatform(await fs.realpath(workDir)),
}
} catch (error) {
return {