Problem
chat-screen.tsx: 3339 lines, 43 useEffect, 26 useRef, 17 useState, ~155 hook calls. The interdependent effects and shared refs (waitingForResponseRef, activeSendRef, ...) are the root enabler of the race/staleness bugs filed separately (#219, #220) — effect ordering coupling no type system checks.
message-item.tsx: 3029 lines, 22-prop MessageItemProps (:279-300), forcing a hand-written 110-line areMessagesEqual comparator (:2913-3025).
Concrete bug in the comparator: areMessagesEqual never compares the attachedToolMessages prop (verified: 0 mentions in the comparator) even though it is consumed at :2372 and memoized into render at :2396. If attachedToolMessages changes while all compared fields are equal, memo skips the re-render → stale tool output displayed. This is the inherent failure mode of hand-maintaining a comparator against a 22-prop surface.
Fix
Found in chat-area audit 2026-06-11. Related: #212, #219, #220.
Problem
chat-screen.tsx: 3339 lines, 43
useEffect, 26useRef, 17useState, ~155 hook calls. The interdependent effects and shared refs (waitingForResponseRef,activeSendRef, ...) are the root enabler of the race/staleness bugs filed separately (#219, #220) — effect ordering coupling no type system checks.message-item.tsx: 3029 lines, 22-prop
MessageItemProps(:279-300), forcing a hand-written 110-lineareMessagesEqualcomparator (:2913-3025).Concrete bug in the comparator:
areMessagesEqualnever compares theattachedToolMessagesprop (verified: 0 mentions in the comparator) even though it is consumed at:2372and memoized into render at:2396. IfattachedToolMessageschanges while all compared fields are equal,memoskips the re-render → stale tool output displayed. This is the inherent failure mode of hand-maintaining a comparator against a 22-prop surface.Fix
attachedToolMessageslength+signature toareMessagesEqual(small, ship first).useChatSend(send/optimistic/error/abort, ~:1320-1410),useLiveToolActivity(EventSource effect ~:842-870),useComposerBusy([frontend] P2: composer busy-state cutover incomplete — dead legacy computation every render + non-reactive getState() read #219),useSessionLifecycle. Target < 800 orchestration lines. Reduce MessageItem prop surface so the default shallow comparator suffices.Found in chat-area audit 2026-06-11. Related: #212, #219, #220.