## Why

The chat UI is the core surface of BooCode, but its streaming layer, code blocks, message list, and shared components have accumulated organic complexity without a unified overhaul. Streaming uses a basic `useSessionStream` + `applyFrame` reducer with manual exponential backoff; `CodeBlock.tsx` lacks line numbers, diff highlighting, and multi-theme support; `MessageList.tsx`'s three-pass pre-render (flatten → group → stampCapHits) works but doesn't virtualize or animate smoothly at scale; and shared components (`MarkdownRenderer`, `MessageBubble`, `ChatInput`, `ToolCallLine`) lack consistent error boundaries, loading skeletons, and accessibility. These together create the highest-impact surface for UX improvement.

## What Changes

- **Streaming layer v2**: Port LangGraph-inspired channel-based state management patterns into the `useSessionStream` reducer architecture — typed channel deltas, predictable frame ordering, interrupt/resume-aware message grafting, and reconnection that preserves mid-stream state instead of a full snapshot-refetch.
- **CodeBlock overhaul**: Add line numbers, diff-highlight mode (gutter markers for `+`/`-` lines), multi-theme toggle (light/dark), word-wrap toggle, collapsible long blocks, and inline copy with progress feedback. Shiki stays as the highlighter.
- **MessageList v2**: Virtualized rendering via `react-virtuoso` for long transcripts, smoother streaming entrance animations (staggered fade-slide per message), smarter tool-call grouping with collapse/expand per group, and message pin/bookmark support (local-only, persisted in URL hash).
- **Component consistency pass**: Error boundaries on `MarkdownRenderer` and `CodeBlock` so Shiki failures don't crash the entire message list; loading skeletons for streaming messages; accessible keyboard navigation in `ToolCallLine` and `ToolCallGroup`; consistent `aria-label` protocol across all `ActionRow` buttons.

## Capabilities

### New Capabilities
- `channel-streaming`: Channel-based streaming reducer inspired by LangGraph Pregel — typed channel deltas (`text`, `tool_call`, `tool_result`, `status`) map to structured states, interrupt/resume-aware mid-stream reconnection, and predictable frame ordering via monotonic seq counters.
- `code-block-pro`: Line numbers, diff gutter markers, multi-theme toggle, word-wrap toggle, collapsible blocks (≥30 lines auto-collapse), inline copy with progress.
- `message-list-v2`: Virtualized rendering for 500+ message transcripts, smarter tool-call grouping with per-group expand/collapse, stagger entrance animations, message pin/bookmark (local via URL hash).
- `component-hardening`: Error boundaries on `MarkdownRenderer`/`CodeBlock`, `Suspense` loading skeletons for streaming content, keyboard-navigable tool calls (`Tab`/`Enter`/`Arrow`), consistent `aria-label` convention.

### Modified Capabilities
*(None — no existing spec files in `openspec/specs/` are changing)*

## Impact

- **`apps/web/src/hooks/useSessionStream.ts`**: Core rewrite from flat `applyFrame` reducer to channel-based dispatch with typed channel deltas.
- **`apps/web/src/components/CodeBlock.tsx`**: Full rewrite — line numbering, diff mode, theme toggle, collapsible.
- **`apps/web/src/components/MessageList.tsx`**: Virtualized rendering, new entrance animation system, smarter grouping.
- **`apps/web/src/components/MessageBubble.tsx`**: Error boundaries, skeleton loading for streaming messages.
- **`apps/web/src/components/MarkdownRenderer.tsx`**: Wrap in error boundary, add `Suspense` fallback.
- **`apps/web/src/components/ToolCallLine.tsx`**, **`ToolCallGroup.tsx`**: Keyboard navigation, aria-labels.
- **`apps/web/package.json`**: New dep `@virtuoso-dev/react-virtuoso` for virtualized list.
- **`@boocode/contracts`**: New channel-delta frame types in `ws-frames.ts` schema for the streaming v2 protocol.
