From 8de6088546dea72addea9ce75b06b977b139af47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 15 Aug 2026 23:29:25 +0000 Subject: [PATCH] the session list was asking for a directory that does not exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Your conversation was there the whole time. The dropdown was empty because I sent the wrong kind of path. what the header sent -Tests-platform no such directory what is on disk -home-pastilhas-Tests-platform 1 transcript Two conventions live next to each other and I used the wrong one. The file endpoints take a HOME-RELATIVE path (`/Tests/platform`) — that is what `cwdToPath` produces and what the file browser and editor pass. `/chat/sessions` takes the ABSOLUTE one, because Claude's folder name is `cwd.replace(/[^a-zA-Z0-9]/g, '-')` applied to the directory a turn actually ran in, and a turn runs in a real directory. `cwdOf` uses the query value verbatim (`query('cwd')?.trim() || home`), so nothing rejected the relative form — it just resolved to a slug for a directory that has never existed, and answered an empty list rather than an error. The failure had no symptom beyond "no conversations", which is also what a genuinely empty directory looks like. Joined against `useChatPwds().defaultCwd` — the caller's home as the SERVER sees it, and the same value `cwdOf` falls back to — rather than guessing at where home is a second time. Verified end to end: `~/Tests/platform` now resolves to `/home/pastilhas/Tests/platform`, whose slug is `-home-pastilhas-Tests-platform`, which is a directory that exists and holds the transcript. tsgo clean, frontend builds, 846 pass / 7 fail unchanged. Co-Authored-By: Claude Opus 5 --- .../officerdev/src/apps/Chat/ChatHeader.tsx | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/workspaces/officerdev/src/apps/Chat/ChatHeader.tsx b/src/workspaces/officerdev/src/apps/Chat/ChatHeader.tsx index edd812fd..dd570c47 100644 --- a/src/workspaces/officerdev/src/apps/Chat/ChatHeader.tsx +++ b/src/workspaces/officerdev/src/apps/Chat/ChatHeader.tsx @@ -7,7 +7,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { useClaudeSessions } from 'state/useClaudeSessions'; +import { useChatPwds, useClaudeSessions } from 'state/useClaudeSessions'; import { useWorkspace, cwdToPath } from '../../components/Workspace'; import { usePanelSession } from './use-panel-session'; import type { ChatMessage } from './types'; @@ -34,10 +34,23 @@ const title = (s: { title: string }) => s.title || '(untitled)'; export const ChatHeader = ({ panelId }: { panelId?: string }) => { const { workspace, cwd } = useWorkspace(); - const cwdPath = cwdToPath(cwd); - // The endpoint takes a home-relative path and treats absent as "the caller's default", which is what an - // unscoped panel wants. - const { sessions, renameSession, deleteSession } = useClaudeSessions(cwdPath === '/' ? null : cwdPath); + + /** + * `/chat/sessions` wants an ABSOLUTE path, and that is not the convention next door. + * + * The file endpoints take a home-relative path (`/Tests/platform`); chat takes the real one, because + * Claude's folder name is `cwd.replace(/[^a-zA-Z0-9]/g, '-')` applied to the directory a turn actually + * ran in. Handing it the home-relative form asked for `-Tests-platform`, which is not a directory that + * exists, so a dashboard with a perfectly good conversation in it listed nothing at all. + * + * `useChatPwds().defaultCwd` is the caller's home as the server sees it — the same value `cwdOf` falls + * back to — so it is the right thing to join against rather than a second guess at where home is. + */ + const { defaultCwd } = useChatPwds(); + const relative = cwdToPath(cwd); + const absoluteCwd = relative === '/' ? null : defaultCwd ? `${defaultCwd.replace(/\/$/, '')}${relative}` : null; + + const { sessions, renameSession, deleteSession } = useClaudeSessions(absoluteCwd); const [sessionId, setSessionId] = usePanelSession(panelId, workspace); const [renaming, setRenaming] = useState(null);