feat(coordinator): frontend store wiring, IPC lifecycle, and session restore (PR 3/4)#124
feat(coordinator): frontend store wiring, IPC lifecycle, and session restore (PR 3/4)#124brooksc wants to merge 1 commit into
Conversation
|
Reviewed the stacked delta from #120 (
|
|
All six issues addressed — in the order you listed them: 1. Blocking: double-spawn loses MCP config Added 2. Blocking: restore spawns terminals before MCP is ready
3. Medium: child tasks lose Added 4. Medium: concurrent coordinator restore races remote server Added a 5. Medium: autosave snapshot missing persisted fields
6. Low: MCP status shape inconsistency
Also cherry-picked two commits from |
|
Additional fixes pushed in ff080ee:
Verification:
|
|
Hey hey! Thank you very much! Looks good to merge, after the conflicts are resolved. A few optional minor nits: Reviewed
None block merge — just drive-by polish. |
|
Pushed What changed:
Verification:
I believe all issues raised in the latest comment are now fully addressed. |
|
Great work @brooksc ! Could you have a look at the conflicts /merge master into the branch? |
|
Ah I think we already merged it as part of the other PRs? @brooksc can you maybe double check? |
13e5bcc to
142652c
Compare
|
Double-checked this after rebasing The original PR124 stack has effectively already landed through the later coordinator stack / PRs on That guard is valid, but it only protects a narrow manual retry race. The normal restore path already starts coordinators first, waits for coordinator restore promises, and only hydrates children whose coordinator is Closing this PR as superseded by the later coordinator work on |
|
Closing as superseded by the later coordinator stack already merged to main. |
Overview
This is PR 3 of 4 in the coordinator series splitting #100 as requested in the round-4 review. It is stacked on PR 2 (#120) (
coordinator-2-mcp-backend) and must be merged after that one. The diff shown here includes PRs 1–2's content; the meaningful delta for this PR is the frontend store wiring and IPC lifecycle described below.PR sequence:
coordinator-1-securitycoordinator-2-mcp-backendcoordinator-3-store-ipccoordinator-4-uiNothing coordinator-related is user-visible until PR 4 adds the
NewTaskDialogcheckbox and Settings toggle (coordinatorModeEnableddefaults tofalse).What's in this PR (delta over PR 2)
Task model extensions (
src/store/types.ts)New fields on
Task/CollapsedTask:coordinatorMode,coordinatedBy,controlledBy('coordinator' | 'human'),propagateSkipPermissionsmcpConfigPath,mcpStartupStatus('pending' | 'ready' | 'error'),mcpStartupErrorsignalDoneReceived,signalDoneAt,signalDoneConsumed,needsReview,initialPromptstagedNotification: StagedNotification— pending coordinator batch notificationcoordinatorModeEnabled,coordinatorNotificationDelayMs,coordinatorControlHintDismissedMCPStatustype for frontend pollingMCP lifecycle (
src/store/tasks.ts)New store functions wired to IPC:
initMCPListeners()— subscribes to 7 MCP push events (MCP_TaskCreated,MCP_TaskClosed,MCP_TaskCleanupFailed,MCP_CoordinatorNotificationStaged/Cleared/Orphaned,MCP_TaskStateSync,MCP_TaskHydrated)markTaskMcpPending/Ready/Error()— MCP startup state transitionsretryTaskMcpStartup()— retry after error, with coordinator-dependency guardsetTaskControl()— togglescontrolledBybetween'coordinator'and'human'clearStagedNotification(),setStagedNotificationUserEdited()getCoordinatorCloseWarning()— warning text when closing a coordinator with active childrenreorderTaskVisually()— drag-reorder respecting coordinator group boundariescollapseTask()guard — no-op for coordinated children (prevents orphaning from coordinator group)closeTask()— callsMCP_CoordinatedTaskClosed/MCP_CoordinatorDeregisteredon close; errors swallowed so the task is always removedSession restore (
src/App.tsx)On startup, iterates all persisted tasks with
coordinatorMode === trueand callsStartMCPServerfor each, rewriting.mcp.jsonconfig with the new session's port and token. Child tasks start withmcpStartupStatus: 'pending'(terminal spawn deferred) untilMCP_HydrateCoordinatedTaskreturns.Persistence (
src/store/persistence.ts)saveState()/loadState()now round-trips all coordinator fields:coordinatorMode,coordinatedBy,controlledBy,propagateSkipPermissions,mcpConfigPath,signalDone*,needsReview. Global fieldscoordinatorModeEnabled,coordinatorNotificationDelayMs,coordinatorControlHintDismissedalso persisted. Tasks withcoordinatorModeorcoordinatedByload withmcpStartupStatus: 'pending'to defer terminal spawn until the session's MCP server is ready.Coordinator preamble (
src/store/coordinator-preamble.ts)System preamble prepended to the coordinator agent's initial prompt. Documents all 10 MCP tools and operating rules.
MCP status polling (
src/store/mcpStatus.ts)Polls
GetMCPStatusIPC every 3 s; stores result instore.mcpStatus. Returns{ running: false, ... }when no coordinator is active — no user-visible effect until a coordinator task exists.Staged notification store (
src/store/taskStatus.ts)getTaskStatus()/getTaskDotStatus()return'review'for tasks withneedsReviewset. Replace-on-arrival: new batch replaces the previous one before it fires;userEditedresets per-notification (not sticky).Backend fix (
electron/mcp/coordinator.ts)In
deregisterCoordinator, child tasks that never received their prompt (assignedPromptDeliveredfalse) havereviewNotificationQueuedset — suppresses a spurious "needs review" notification for tasks the user never saw.IPC preload (
electron/preload.cjs)New channels exposed:
set_coordinator_mode_enabled,mcp_hydrate_coordinated_task,mcp_task_hydrated,mcp_coordinated_task_closed,mcp_task_cleanup_failed.Tests
src/store/tasks.test.tscontrolledBystate machine,MCP_TaskCreatedhandler,collapseTaskchild guard,hasActiveCoordinator, MCP startup transitions (pending/ready/error/retry/child-failure),MCP_TaskCleanupFailed,closeTaskIPC orderingsrc/store/notifications.test.tsuserEditedper-notification reset,clearStagedNotificationsrc/store/taskStatus.test.tsgetTaskStatus/getTaskDotStatusreturn'review'forneedsReview; busy overrides reviewsrc/store/persistence.test.tselectron/mcp/docker.integration.test.tscoordinatorTaskIdchanged from'coord-1'to a valid UUID constantAssumptions and important notes
getCoordinatorChildren/isCoordinatedChildfromsidebar-order.tsbecause PR 2 provides them. Without PR 2, the app will not typecheck.mcpStatus.tspolls every 3 s as soon as this lands, but returns empty status until a coordinator task exists.StartMCPServercalls fire in parallel for all coordinator tasks on startup. Each child staysmcpStartupStatus: 'pending'until hydration completes. The UI for that state lives in PR 4.agentId(parallel-code-{agentId.slice(0,12)}). The container is likely dead after restart; wiring exists so the user can manually restart the coordinator agent.coordinatorModeEnableddefaults tofalse. No user-visible change until PR 4 adds the Settings checkbox.🤖 Generated with Claude Code