A run could get stranded at 'running' in the UI after a crash/disconnect/
restart, with no way to clear it. Root cause was a race: the SSE history
replay re-asserted a stale `running` status that beat the poll's settled
status, leaving the run showing "Running" + the settle error at once.
Server (runs.ts / runner.ts / index.ts):
- reconcile() on every read force-settles any 'running' run with no live
runner, so the board self-heals on the next poll (≤3s) — no restart needed.
- forceSettle() emits a persisted `status` event so an open/reconnecting
SSE stream replays the terminal state last, not a stale `running`.
- Startup orphan-reconciliation now also emits that event (was the gap that
let the replay re-assert `running` after a server restart).
- Idle watchdog (10min): a silent pi is settled as 'failed' instead of
hanging forever; SIGKILL escalation (20s) reaps wedged processes.
- stop() now recovers: active→abort, orphaned-but-running→force-stop
(the Stop button clears wedged runs instead of 409'ing).
- start() catch force-settles 'failed' so a spawn failure never orphans a
half-created 'running' row.
Client (useOrchestrator.ts):
- patchRun refuses to un-settle a terminal run, dropping stale replayed
status as a belt-and-suspenders guard against any such race.
EOF && echo "" && git log --oneline -3
Two intertwined changes that both touch the orchestrator hook + run console:
Isolate the agent event stream (perf):
- useRunStream owns the SSE stream + event log locally inside AgentRunBar, so a
burst of streamed events re-renders only the console — never the board page or
card modal (which was causing frame drops at run start).
- useOrchestrator is now a registry only; lifecycle events reflect back up via
stable patchRun/reflectBevy reflectors (effect deps depend on those, not the
whole object, avoiding a stream-teardown loop).
Session resume for Refine:
- Runs now persist their pi session (drop --no-session); each fresh run captures
its session JSONL path into a new agent_runs.session_file column (additive,
idempotent migration).
- Refine resumes the prior run's actual session (--session <path> → appends) in
that run's own worktree (inherited, never owned), sending the operator's
feedback as the next message in the same conversation with full prior context.
- owns_worktree guards remove()/cleanup so a refinement never destroys the
owning run's worktree; bad refinement targets return 409.
- AgentRunBar shows Refine only for settled runs with a recorded session.
EOF && echo "" && git log --oneline -3
Memoize the board so streaming events from an active run no longer force
all 38 collapsed cards to re-render — only the open CardModal/AgentRunBar
re-renders, since its props stay referentially equal.
- KanbanCard: wrap in React.memo; take isRunning/bevyRunning primitives
instead of the whole orchestrator object; onOpen now takes the card id.
- useOrchestrator: expose a memoized activeByCard map, recomputed only
when runs/bevyRunning change (not on every streamed text/tool event).
- KanbanBoardPage: pass a stable openById callback + primitive props so
memo bails; memoize StatCard; extract the static category legend to a
memoized CategoryLegend component.
Also drops the per-card .filter().find() status scans each render.
- RunEventList: grouped activity timeline. Assistant text becomes chat
bubbles (auto-collapsing long messages); tool_start/tool_end pair into
entries with spinners and expandable input/result blocks; bevy output
rolls into a live console; relative timestamps on a left rail
- AgentRunBar: redesigned as a mission console. Live stats header (elapsed
time, tool count, events), animated status banner with sweep/glow while
running, clearer action bar. All controls preserved (run/steer/stop,
diff/merge/bevy) so the human-only merge/complete safety model holds
- tailwind.css: vn-flow, vn-sweep, vn-dots, vn-spin keyframes
- CardModal: full card overlay (orchestrator, references, tags, comments)
- DiffModal: branch-diff review (commits, stat, capped patch)
- useOrchestrator: background polling + bevy status sync + ref-counted SSE
- KanbanCard: pulsing agent/bevy running badge on collapsed cards
Replace the localStorage kanban with the backend-backed board, add typed clients
and a React hook with optimistic updates. Cards can reference static doc pages
and user-created custom pages (new "custom" reference type with purple chips).
Add the agentic orchestrator UI: a per-card panel to launch `pi` runs, watch a
live tool/thought stream over SSE, steer mid-run, and stop — while the board
stays fully interactive. The board page wires the orchestrator and custom-pages
stores into every card.