diff --git a/web/src/components/SessionList.directory-action.test.tsx b/web/src/components/SessionList.directory-action.test.tsx
index 2b76e43201..f5243d266f 100644
--- a/web/src/components/SessionList.directory-action.test.tsx
+++ b/web/src/components/SessionList.directory-action.test.tsx
@@ -1,4 +1,4 @@
-import { cleanup, fireEvent, render, screen } from '@testing-library/react'
+import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { ReactNode } from 'react'
@@ -95,3 +95,102 @@ describe('SessionList directory action', () => {
expect(screen.queryByRole('button', { name: 'New session in this directory' })).toBeNull()
})
})
+
+describe('SessionList collapse behavior', () => {
+ function renderSessionList(sessions: SessionSummary[], selectedSessionId = 'session-running') {
+ return (
+
+
+
+
+
+ )
+ }
+
+ function getProjectPanel(): Element {
+ const header = screen.getByTitle('/work/hapi')
+ const panel = header.nextElementSibling
+ if (!panel) {
+ throw new Error('Expected project collapse panel')
+ }
+ return panel
+ }
+
+ it('keeps a selected running path collapsed across live session-list refreshes', async () => {
+ const baseSessions = [
+ makeSession({
+ id: 'session-running',
+ active: true,
+ thinking: true,
+ pendingRequestsCount: 1,
+ updatedAt: 100,
+ metadata: { path: '/work/hapi', name: 'Running task', flavor: 'codex' },
+ }),
+ makeSession({
+ id: 'session-old',
+ updatedAt: 50,
+ metadata: { path: '/work/hapi', name: 'Older task', flavor: 'codex' },
+ })
+ ]
+ const { rerender } = render(renderSessionList(baseSessions))
+
+ expect(getProjectPanel().getAttribute('data-open')).toBe('true')
+
+ fireEvent.click(screen.getByTitle('/work/hapi'))
+ expect(getProjectPanel().getAttribute('data-open')).toBeNull()
+
+ rerender(renderSessionList([
+ {
+ ...baseSessions[0]!,
+ pendingRequestsCount: 2,
+ updatedAt: 200,
+ },
+ baseSessions[1]!
+ ]))
+
+ await waitFor(() => {
+ expect(getProjectPanel().getAttribute('data-open')).toBeNull()
+ })
+ })
+
+ it('auto-expands the path again when the selected session changes', async () => {
+ const sessions = [
+ makeSession({
+ id: 'session-running',
+ active: true,
+ thinking: true,
+ updatedAt: 100,
+ metadata: { path: '/work/hapi', name: 'Running task', flavor: 'codex' },
+ }),
+ makeSession({
+ id: 'session-next',
+ updatedAt: 90,
+ metadata: { path: '/work/hapi', name: 'Next task', flavor: 'codex' },
+ })
+ ]
+ const { rerender } = render(renderSessionList(sessions))
+
+ fireEvent.click(screen.getByTitle('/work/hapi'))
+ expect(getProjectPanel().getAttribute('data-open')).toBeNull()
+
+ rerender(renderSessionList(sessions, 'session-next'))
+
+ await waitFor(() => {
+ expect(getProjectPanel().getAttribute('data-open')).toBe('true')
+ })
+ })
+})
diff --git a/web/src/components/SessionList.tsx b/web/src/components/SessionList.tsx
index 294fcc364b..8b48a86c51 100644
--- a/web/src/components/SessionList.tsx
+++ b/web/src/components/SessionList.tsx
@@ -739,6 +739,7 @@ export function SessionList(props: {
const [collapseOverrides, setCollapseOverrides] = useState