the chat panel's header names a new conversation too

The list learned the name and the panel header did not, which narrowed it to one lookup: the panel records
its session id, and ChatHeader resolves the title by finding that id in the sessions LIST.

The panel was recording `chat.sessionId` — OFFICER's session key — and the list is keyed on Claude's
TRANSCRIPT id. Two id spaces on either side of one `find`, so it never matched: a new chat's header read
"New chat" for the whole life of the conversation and offered no rename, because rename is a per-row action
on a list the session was not found in. Selecting the conversation from the list fixed it, which is exactly
why navigating away and back looked like the cure.

Same trap as the Command HQ pane on 2026-08-21, third time this pair has caused a bug: officer's handle and
the harness's transcript id are not interchangeable, and every place that keys one lookup on the other fails
silently rather than loudly.

The "only a REAL id, never null" rule is unchanged and still needed — both ids are null for a moment while a
resumed session connects, and writing that null would turn "this panel has never chosen" into "the user asked
for a new chat", so the next reload would open blank.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 18:35:09 +00:00
co-authored by Claude Opus 5
parent 577ebde902
commit 3f1ba38f19
@@ -84,16 +84,23 @@ const ChatPanelInner = ({
useEffect(() => {
setActiveSession(chat.sessionId);
// Only a REAL id, never null.
//
// A new chat is nameless until the harness names it on the first turn, and recording that id is what
// makes "New chat, say something, reload" return to that conversation instead of a third one. But
// `chat.sessionId` is also null for a moment while a resumed session connects — and writing that null
// would turn "this panel has never chosen" into "the user asked for a new chat", so the next reload
// would open blank. Asking for a new conversation is something the header says explicitly; this
// effect only ever reports what the harness decided.
if (chat.sessionId) onSessionId?.(chat.sessionId);
}, [chat.sessionId]);
/*
`claudeSessionId`, NOT `sessionId`. The panel's stored id is looked up in the sessions LIST — see
`ChatHeader`'s `sessions.find((s) => s.id === sessionId)` — and that list is keyed on Claude's
transcript id. Recording officer's own session key here put two id spaces on either side of one
lookup: it never matched, so a new chat's header read "New chat" for the life of the conversation and
offered no rename, because rename is a per-row action on a list the session was not found in.
Selecting the conversation from the list fixed it, which is why navigating away and back appeared to
be the cure.
Only a REAL id, never null. A new chat has none until the harness names its transcript on the first
turn, and recording it is what makes "new chat, say something, reload" return to that conversation
rather than a third one. Both ids are also null for a moment while a resumed session connects, and
writing that null would turn "this panel has never chosen" into "the user asked for a new chat", so
the next reload would open blank.
*/
if (chat.claudeSessionId) onSessionId?.(chat.claudeSessionId);
}, [chat.sessionId, chat.claudeSessionId]);
return (
<div className="flex h-full min-h-0 flex-col">