diff --git a/src/workspaces/officerdev/src/apps/CommandHQ/CommandHqDetail.tsx b/src/workspaces/officerdev/src/apps/CommandHQ/CommandHqDetail.tsx index eb0eb709..6583d7bb 100644 --- a/src/workspaces/officerdev/src/apps/CommandHQ/CommandHqDetail.tsx +++ b/src/workspaces/officerdev/src/apps/CommandHQ/CommandHqDetail.tsx @@ -41,20 +41,28 @@ function EmptyState() { function NewCommander({ category }: { category: number | 'none' | undefined }) { const navigate = useNavigate(); const chat = useChat(undefined, undefined, { replaceUrl: false, context: 'command-hq' }); - const { setCategory } = useCommandHqCommanderActions(chat.sessionId ?? ''); + // Also the transcript id: `command_hq_sessions` is keyed on it, so an action addressed by officer's key + // updates nothing and says nothing. Same root cause as the navigate below. + const { setCategory } = useCommandHqCommanderActions(chat.claudeSessionId ?? ''); const [filed, setFiled] = useState(false); useEffect(() => { - if (!chat.sessionId || filed) return; + // `claudeSessionId`, NOT `sessionId`. The commander is filed under Claude's transcript id + // (`fileAsCommander` in websocket.ts), and this URL has to match or the detail pane fetches an id + // nothing knows. `sessionId` arrives FIRST — `session:init` sets it to officer's own key — so + // navigating on it sent the page to an id that 404s, and `filed` then blocked the correction when the + // real one arrived. A commander that had run and answered correctly rendered as "Send a message to + // start". Waiting for the transcript id costs one more round trip and is the only id that works. + if (!chat.claudeSessionId || filed) return; setFiled(true); // `fileAsCommander` (websocket.ts) always files with no category — it has no way to know one was // requested from here. The same race the `/commanders/new` HTTP route resolves server-side by always // running its UPDATE last; done client-side here for the same reason, since the web composer goes // through the websocket path instead. if (typeof category === 'number') void setCategory.mutateAsync(category).catch(() => {}); - navigate(commandHqSessionPath(chat.sessionId, category), { replace: true }); + navigate(commandHqSessionPath(chat.claudeSessionId, category), { replace: true }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [chat.sessionId, filed]); + }, [chat.claudeSessionId, filed]); return (