fix(ai-chat): reset chat on New chat during the first turn's stream (#161) #162

Closed
Ghost wants to merge 3 commits from fix/ai-chat-new-chat-during-stream into develop

3 Commits

Author SHA1 Message Date
claude code agent 227
8f9d7fe0bd test(ai-chat): cover the #161 New-chat-during-stream reset + changelog
Address PR #162 review must-fixes:

- CHANGELOG: add an [Unreleased] > Fixed bullet for #161 (New chat during
  the first turn's stream now resets the thread; a late refetch/onFinish
  from the abandoned thread no longer pulls the user back in).
- Re-anchor the stale startNewChat/cancelPendingAdoption test to selectChat:
  startNewChat now calls startFreshThread, but cancelPendingAdoption still
  backs selectChat, so the disarm guard is decoupled and kept valid.
- Add a test pinning that startFreshThread() disarms the armed error-path
  fallback (the justification for dropping cancelPendingAdoption from
  startNewChat).
- Add a ChatThread render test (mocked useChat) asserting the onError branch
  forwards onTurnFinished(undefined, threadKey).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:59:09 +03:00
claude_code
4d69518037 Merge branch 'develop' into fix/ai-chat-new-chat-during-stream (#162)
Resolve the PR #162 review blocker: the branch no longer merged into
develop after #174 (onServerChatId early adoption) and #183
(server-sourced Copy/export) landed, which touched the same files.

Conflict resolution (ai-chat-window.tsx, chat-thread.tsx,
use-chat-session.ts):
- Keep develop's onServerChatId wiring (#174) intact in all three files.
- Keep develop's removal of the old liveStateRef / liveThreadRef /
  MutableRefObject live-export mechanism (deleted by #174/#183); it was
  only inherited by this branch and is not part of #161 — not resurrected.
- Graft #162's actual fix on top: startFreshThread() (unconditional
  remount even when activeChatId stays null) and the abandoned-thread
  guard (threadKeyRef + finishingThreadKey on onTurnFinished, forwarded
  as ChatThread's threadKey through onFinish/onError).

Also apply the review's simplification: drop the now-redundant
cancelPendingAdoption() call inside startNewChat (startFreshThread()
already nulls pendingNewChatRef); the export stays, still used by
selectChat.

ai-chat client suite green (176 passed), no conflict markers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 22:34:38 +03:00
claude code agent 227
cee231b136 fix(ai-chat): reset chat on New chat during the first turn's stream (#161)
Pressing "New chat" while a brand-new chat's first turn was still streaming
only removed the role badge — the thread, session and stream were kept. The
thread remount is driven by a render-phase reconciler in useChatSession that
fires only when activeChatId !== thread.chatId; on a not-yet-adopted new chat
both are null, so startNewChat's setActiveChatId(null) was a no-op and nothing
remounted.

- useChatSession: add startFreshThread(), which unconditionally dispatches a
  reconcile to a fresh mount key so ChatThread remounts even when activeChatId
  stays null. startNewChat now calls it.
- Guard against the abandoned thread's late finish: ai@6's useChat does not
  abort on unmount and proxies its callbacks, so onFinish/onError of the chat
  the user just left still fire and would adopt it back. onTurnFinished now
  takes the finishing thread's key and, when it no longer matches the mounted
  thread, only refreshes the chat list — no adoption, no fallback arming, no
  per-chat message invalidation. ChatThread forwards its threadKey in both
  onFinish and onError. An omitted key (legacy callers/tests) counts as the
  current thread, preserving the #137 adoption behavior.

Tests: 3 new useChatSession cases (fresh-thread remount with activeChatId null,
abandoned-thread finish rejected, current-thread finish still adopts). Full
ai-chat suite green (183).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:43:07 +03:00