Compare commits

...
2 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 59f600d34d a new chat gets its name without a refresh — this time with the import
Re-lands f9a36996, which I reverted because it broke chat rendering outright:
`ReferenceError: useQueryClient is not defined`.

The cause was mine and it is embarrassing in a specific way. The edit that added the import searched for
`import { useCallback, useEffect, useMemo, useRef, useState } from 'react'` and the file's first line is
`import { useState, useEffect, useRef, useCallback } from 'react'` — different order, and useMemo is not in
it. So the search matched nothing, and I had used replace WITHOUT an assertion, so nothing said so. That is
the identical failure I caught and wrote up hours earlier in the CommandHQ layout edit, where two edits
silently did nothing for the same reason.

Every edit in this commit asserts. Which then caught a second, subtler fault: `commitStreaming()` appears
ELEVEN times, so anchoring the turn-end call on it put announceSession inside `assistant:text` rather than
`result`, with mangled indentation. An assert proves an anchor EXISTS; it does not prove it is unique. The
result handler is now anchored on a three-line sequence that occurs once, and the count is asserted rather
than assumed.

Verified in the BUILT BUNDLE rather than by typecheck, because a ReferenceError is a bundle fact: the name
appears zero times as a free identifier (the bundler resolved and renamed it), and both invalidation keys are
present — CLAUDE_SESSIONS went from one occurrence to two, the second being this.

Worth recording about the tooling, since I doubted it: tsgo DOES catch this class of error. I probed it three
times with a deliberate type error in this exact file and it reported every time. So the check is sound and
the miss was mine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 18:26:15 +00:00
pastilhas cbedfed2ba Revert "a new chat gets its name without a refresh, and can be renamed"
This reverts commit f9a3699603.
2026-08-22 18:18:54 +00:00
@@ -1,4 +1,5 @@
import { useState, useEffect, useRef, useCallback } from 'react'; import { useState, useEffect, useRef, useCallback } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { useChatWebSocket } from 'hooks/useChatWebSocket'; import { useChatWebSocket } from 'hooks/useChatWebSocket';
import { useClient } from 'hooks/useClient'; import { useClient } from 'hooks/useClient';
import { useSettings } from 'state/useSettings'; import { useSettings } from 'state/useSettings';
@@ -214,7 +215,7 @@ export function useChat(initialSessionId?: string, initialModel?: string | null,
Fired when the transcript id arrives — the START of the turn, not the end — because that is the moment Fired when the transcript id arrives — the START of the turn, not the end — because that is the moment
the session becomes real on disk and the server can already name it from the first user message. Fired the session becomes real on disk and the server can already name it from the first user message. Fired
again when the turn finishes, because the harness's own `ai-title` may have improved on it by then. again when a turn finishes, because the harness writes its own `ai-title` while the turn runs.
*/ */
const announceSession = useCallback(() => { const announceSession = useCallback(() => {
void queryClient.invalidateQueries({ queryKey: ['CLAUDE_SESSIONS'] }); void queryClient.invalidateQueries({ queryKey: ['CLAUDE_SESSIONS'] });
@@ -430,10 +431,6 @@ export function useChat(initialSessionId?: string, initialModel?: string | null,
setClaudeSessionId(msg.claudeSessionId); setClaudeSessionId(msg.claudeSessionId);
if (replaceUrl) writePermalink(msg.claudeSessionId); if (replaceUrl) writePermalink(msg.claudeSessionId);
} }
// Again at the end of every turn, and OUTSIDE the id check above: the harness writes its own
// `ai-title` while the turn runs, so the name the list is showing may now be bettered by the one on
// disk — and that is true whether or not this particular `result` happened to carry the id.
announceSession();
commitStreaming(); commitStreaming();
setMessages((prev) => [ setMessages((prev) => [
...prev, ...prev,
@@ -446,6 +443,9 @@ export function useChat(initialSessionId?: string, initialModel?: string | null,
const hadTools = toolCallsInTurnRef.current; const hadTools = toolCallsInTurnRef.current;
toolCallsInTurnRef.current = false; toolCallsInTurnRef.current = false;
onTurnCompleteRef.current?.(hadTools); onTurnCompleteRef.current?.(hadTools);
// Every turn end: the harness writes its own `ai-title` while a turn runs, so the name the lists are
// showing may now be bettered by the one on disk.
announceSession();
break; break;
} }