---
phase: 23-brainstormer-flow
plan: "03"
subsystem: ui
tags: [integration, chat, messageType, dispatch, handoff, brainstormer-default]
dependency_graph:
  requires: ["23-01", "23-02"]
  provides: ["ChatMessage messageType dispatch", "ChatMessageList propagation", "chatApi.handoffSpec", "chatApi.postStatusUpdate", "ChatPanel brainstormer default + handoff callback"]
  affects:
    - ui/src/components/ChatMessage.tsx
    - ui/src/components/ChatMessageList.tsx
    - ui/src/components/ChatPanel.tsx
    - ui/src/api/chat.ts
tech_stack:
  added: []
  patterns:
    - "messageType dispatch block in ChatMessage before role check"
    - "useBrainstormerDefault auto-selection on new conversations (activeConversationId === null proxy)"
    - "handleHandoff useCallback with queryClient.invalidateQueries + pushToast on error"
key_files:
  created: []
  modified:
    - ui/src/components/ChatMessage.tsx
    - ui/src/components/ChatMessageList.tsx
    - ui/src/components/ChatPanel.tsx
    - ui/src/api/chat.ts
decisions:
  - "Used activeConversationId === null as proxy for 'new conversation' in brainstormer auto-select (messages not needed)"
  - "Used useToast() / pushToast() for error toast (sonner not used in codebase; project uses custom ToastContext)"
  - "Streaming synthetic message already had messageType: null in ChatMessageList — no change needed"
metrics:
  duration_minutes: 5
  completed_date: "2026-04-01"
  tasks_completed: 2
  files_modified: 4
---

# Phase 23 Plan 03: Chat Integration Wiring Summary

Wire all Phase 23 components into the existing chat pipeline: messageType dispatch in ChatMessage, prop propagation in ChatMessageList, brainstormer default auto-select in ChatPanel, and handoff/status-update API methods in chatApi.

## Tasks Completed

| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 | ChatMessage dispatch, ChatMessageList propagation, chatApi handoff method | c7a48bc5 | ChatMessage.tsx, ChatMessageList.tsx, chat.ts |
| 2 | ChatPanel brainstormer default wiring and handoff callback | 3f575453 | ChatPanel.tsx |

## Changes Made

### Task 1: ChatMessage, ChatMessageList, chatApi

**ChatMessage.tsx:**
- Added `messageType?: string | null` and `conversationId?: string` and `onHandoff?` props
- Added messageType dispatch block before the `role === "user"` check that routes to `ChatSpecCard`, `ChatHandoffIndicator`, `ChatTaskCreatedBadge`, `ChatStatusUpdateBadge` based on `messageType`
- Falls through to default markdown rendering if no messageType matches

**ChatMessageList.tsx:**
- Added `onHandoff?` prop to `ChatMessageListProps`
- Passes `messageType={msg.messageType}`, `conversationId={conversationId}`, and `onHandoff={onHandoff}` to `<ChatMessage>`
- Synthetic streaming entry already had `messageType: null` — no false dispatch possible

**chat.ts:**
- Added `handoffSpec(conversationId, spec, targetRole)` — POSTs to `/conversations/:id/handoff`
- Added `postStatusUpdate(conversationId, data)` — POSTs to `/conversations/:id/status-update`

### Task 2: ChatPanel

- Imported `useBrainstormerDefault` hook and `useToast` context
- Called `useBrainstormerDefault()` to get the general agent ID
- Added `useEffect` to auto-select brainstormer when `activeAgentId === null` and `!activeConversationId` (new conversation proxy)
- Added `handleHandoff` useCallback that calls `chatApi.handoffSpec`, invalidates messages cache, and shows error toast via `pushToast` on failure
- Passed `onHandoff={handleHandoff}` to `<ChatMessageList>`

## Verification

- TypeScript: `pnpm exec tsc --noEmit -p ui/tsconfig.json` — PASSED (no errors)
- Tests: All pre-existing test failures confirmed pre-existing (skill-registry, hmr-port, plugin-worker-manager, company-import-export). No new failures introduced.

## Deviations from Plan

### Auto-fixed Issues

None.

### Observations

1. **ChatMessageList already had messageType: null** — The synthetic streaming entry already contained `messageType: null` from Plan 02's implementation. Task 1 confirmed this and passed the messageType through to ChatMessage without any additional changes needed to the streaming entry.

2. **Toast library** — Plan suggested using `toast.error()` from sonner. The codebase uses a custom `ToastContext` with `pushToast({ title, tone: "error" })`. Used the project's actual pattern instead of sonner.

3. **brainstormer auto-select proxy** — Plan offered two options: messages-based or `!activeConversationId`. Since `messages` was already available from `useChatMessages(activeConversationId)` in ChatPanel, either approach was valid. Used `!activeConversationId` as the simpler, more semantically correct proxy (new conversation = no ID yet).

## Known Stubs

None — all wiring is complete. The handoff route was implemented in Plan 01 (server-side). The components were implemented in Plan 02. This plan connects them.

## Self-Check: PASSED
