3b2e1396fa
* perf(kanban): memoize derived board data to stop re-render storms The board rebuilt the `tasks` array (and `stepOptions`/`sharedProps`) on every store change, producing fresh object identities that defeated downstream memos and re-rendered every card on each WebSocket message. Wrap them in useMemo so the board only re-renders when its inputs actually change. * perf(session-runtime): bound runtime memory and dedupe git-status compare Three runtime-store fixes for the crash a heavy rebase triggered: - Cap shell + terminal output buffers at 2MB (matching the existing process buffer) so a chatty shell can't grow the store without limit. - Purge per-session runtime state (messages, turns, shell/process buffers, and the bySessionId maps) when a session is removed; env-scoped maps are dropped only once no other session shares the environment. - Run setGitStatus's deep per-file diff comparison once per update and return whether anything changed, instead of recomputing it 2-3x. Under a massive rebase (thousands of files, frequent updates) that comparison was the dominant CPU cost and drove the render storm. * refactor(session-runtime): drop now-unused gitStatusWouldMutate The git-status handler no longer pre-checks for changes (setGitStatus now returns whether it changed), leaving gitStatusWouldMutate with no callers. * fix(session-runtime): enforce terminal cap on first and oversized chunks appendTerminalChunk only evicted when more than one chunk was buffered, so a single chunk larger than the 2MB cap slipped through untrimmed; the new-terminal branch also pushed the first chunk without going through the cap at all. Clamp each chunk to the tail of the cap and route the new-terminal path through appendTerminalChunk. Addresses CodeRabbit review.