feat(coordinator): Eliminate the need for take/release control#149
feat(coordinator): Eliminate the need for take/release control#149brooksc wants to merge 6 commits into
Conversation
Multi-Agent Review — PR #149Seven independent reviewers analyzed the third commit only (5ac9b36, excluding the precursors in #138 + #145): six Claude sub-agents (Correctness, Security, Architecture, Alternatives, Performance, Simplicity) + Codex CLI. Findings cross-validated against the actual diff. Critical (verified, multi-reviewer consensus)
Warnings
Suggestions
Strengths
Reviewer Agreement Matrix
VerdictNeeds changes before merge — High confidence, majority consensus. The two critical blockers are (1) the initial-prompt-delivery gap that bypasses the activity lease, and (2) the stuck Also: still stacked on #138 + #145. Once those merge, the effective review surface here shrinks ~10× and a second pass against a clean base would be valuable. |
Response to Multi-Agent ReviewAddressing each numbered finding and suggestion in order. Critical1. Initial prompt delivery bypasses human-control / activity lease 2. 3. Backspace dead code / latent stuck bug Warnings4. Initial-prompt write not serialized against concurrent 5. Manual send doesn't clear local staged notification 6. Per-keystroke reactive churn 7. 8. Dangerous-flag real-CLI integration test 9. Prompt-echo suppression unbounded 10. No size cap on 11. SuggestionsDeduplicate
Collapse Inline Module-level singleton guards, Regression tests added (third-pass feedback)
|
Multi-Agent Review — Round 2 (PR #149)Six independent reviewers (Correctness, Security, Architecture, Alternatives, Performance, Simplicity). Codex CLI failed to start in the sandbox — excluded. ContextPR head is still Round-1 criticals — still unfixed in
|
Users can now type into any coordinator or sub-task session without first taking control. Coordinator and system prompts queue and deliver automatically when the target session is safe: agent idle, no user draft, no pending terminal input, no recent user activity. Backend (electron/mcp): - Backend-owned initial prompt delivery with scheduling/retry so sub-tasks no longer depend on a mounted renderer panel. - Improved terminal prompt detection: Working(...), background terminal running, q-corrupted variants, trust dialogs. - New 'landed' PendingNotification state so successful land_self queues a "self-landed successfully" notification. - Suppress coordinator notifications while children are still active. Renderer: - Per-tick autofire arbiter with explicit wait reasons (paused, waiting-for-user-draft, waiting-for-terminal-input, waiting-for-user-activity, no-prompt, fire). - Prompt draft and terminal pending-input tracking so queued automation can't overwrite half-typed user input. - Per-task user activity leases — typing pauses automation without taking durable ownership. - Removed Take/Release control UI from TaskPanel. Tooling: - scripts/check-coordinator-run.mjs — log checker for regression triage from captured app logs. - scripts/fake-agent.mjs — PTY mock for integration tests. - Real-PTY and real-CLI integration suites for Codex/Claude/Gemini /Copilot. Dead-code cleanup: - Removed src/lib/terminalDisableStdin.ts; both helpers had become constant-wrapping shims after the take/release removal. OpenSpec: new capability spec session-prompt-delivery. Stacked on johannesjo#138 (terminal PTY remount lifecycle) and johannesjo#145 (sub-task self-landing). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Critical fixes: - Gate tryDeliverInitialPrompt on controlMap === 'human'; reschedule instead of writing through while the user has the keyboard - Clear terminalInputPending when questionActive transitions true→false so self-resolving agent questions don't park the task permanently - Remove dead backspace branch in nextTerminalInputPending Warnings: - Clear local staged notification before MCP_CoordinatorRestageAfterUserSend so stale text can't re-fire on the next autofire tick - Add MAX_PENDING_PROMPTS (32) and MAX_PROMPT_BYTES (64KB) caps to sendPrompt - Add writingPromptTaskIds lock to serialize concurrent sendPrompt against tryDeliverInitialPrompt / flushNextQueuedPrompt writes - Prune activityReleaseTimers in removeTaskFromStore to prevent timer leaks - Debounce userActivityHoldUntil updates to >250ms extensions only to reduce per-keystroke reactive churn - Replace suppressNextIdleConsumed boolean with suppressNextIdleCount; warn after 10 suppressions per delivery rather than blocking after one - Add RUN_REAL_AGENT_DANGEROUS guard for dangerous-flag integration tests; truncate getTaskOutput in failure messages to 2KB - Comment the load-bearing if (prev === who) return guard in setTaskControl - Use unshift() on flushNextQueuedPrompt error path for consistency Suggestions: - Import AGENT_READY_TAIL_PATTERNS from prompt-detect.ts in taskStatus.ts instead of duplicating - Consolidate waiting-for-user-draft / waiting-for-terminal-input / waiting-for-user-activity into a single 'waiting' AutoFireTickResult outcome - Inline shouldKeepWaitingForInitialPromptOutput (1-line wrapper) into its callsite; update test to use normalizeCurrentFrame directly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eedback - sendPrompt: acquire writingPromptTaskIds lock before await to prevent concurrent PTY byte interleaving with tryDeliverInitialPrompt/flush - terminalInputPendingFromQuestion flag: question-set pending is only cleared when a question resolves, not when real user typing arrives - integration test: isolate spawned CLIs in temp HOME to prevent host dotfile/credential exposure during dangerous-flag smoke runs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ending flag
- coordinator.test.ts: verify that a concurrent sendPrompt issued while
the first write lock is held returns { queued: true } and both prompts
are flushed in order (not interleaved)
- tasks.test.ts: verify that clearTerminalInputPendingFromQuestion (real
typing) leaves terminalInputPending intact so a subsequent
self-resolving question cannot clear it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5ac9b36 to
f024371
Compare
|
Thanks for the review. I went through the findings in order. Correctness
Maintainability
Performance
Verification:
|
Base / dependencies
This branch sits on top of two precursor PRs that are not yet merged:
Both need to land before this PR. The diff shown here includes both precursors' content; once they merge, this PR's effective diff shrinks to just the changes described below.
Problem
The take/release-control model exposed an internal scheduler concept to the user. Users had to know when a session was "coordinator controlled," when to click Take Control before typing, and when to click Release Control so queued coordinator prompts could be delivered. This was clumsy and caused real failures:
land_selfsilently dropped the coordinator's pending notification with no replacement, so the coordinator never heard about completion.Solution
Live sessions are user-interactive by default. Users can type into any coordinator or sub-task session without first taking control. Coordinator and system prompts are queued and delivered automatically when the target session is safe: agent is idle/ready (not busy, no trust dialog, no interactive question), user has no draft in the prompt input, no pending terminal input, and no recent user activity lease. When the session isn't safe, queued prompts wait. The UI explains the wait in observable terms ("waiting for your draft," "waiting for the agent prompt") instead of "take control."
The persisted
controlledBytask field is preserved as state-restoration data; it no longer gates user input but is still used internally to distinguish coordinator-driven from human-driven sessions.Major changes
Backend (
electron/mcp/,electron/remote/)coordinator.ts(+789 / −124): newscheduleInitialPromptDelivery/tryDeliverInitialPromptwith timers and a "waiting for prompt" state, so background sub-tasks no longer depend on a mounted renderer panel. Also addssendPromptqueueing,flushNextQueuedPrompt, prompt-echo idle suppression, and a newlandedPendingNotificationstate so successfulland_selfqueues a "self-landed successfully" notification instead of silently dropping the pending entry.prompt-detect.ts, +24 / −4): new patterns forWorking (...),background terminal running, q-corrupted variants (qWorking), andDo you truststartup/trust dialogs.coordinator.ts).Renderer (
src/components/,src/lib/,src/store/)autofire-tick.ts(+23 / −7):processAutoFireTickreturns explicit outcomes —too-soon,paused,waiting-for-user-draft,waiting-for-terminal-input,waiting-for-user-activity,no-prompt,fire.prompt-control.ts(+31 / −1):shouldHandoffCoordinatorQuestion,shouldAckInitialPromptDelivery,shouldRendererAutoSendInitialPrompt.prompt-autosend-readiness.ts(new): detects loading-model / booting-MCP startup screens and timeout-abort conditions.prompt-draft.ts(new):hasUserPromptDraftTextdistinguishes a real user draft from a staged-text echo.terminalInputPending.ts(new): tracks typed-but-unsent input by walking the input byte stream; clears on\r,\n,Ctrl-C,Ctrl-U.tasks.ts(+183 / −11) andtypes.ts(+43 / 0): short-lived hold so typing pauses automation without taking durable ownership.TaskPanel.tsx(+53 / −206): take/release control surface deleted; queued-delivery status copy inPromptInput.tsx(+160 / −76).Notifications (
src/store/desktopNotifications.ts, +126 / −13)Tooling
scripts/check-coordinator-run.mjs(215 lines) — log checker that flags initial-prompt gaps, startup-control handoffs, and other regression signals from a captured app log.scripts/fake-agent.mjs(113 lines) — scriptable PTY mock used by the real-PTY suite to exercise Codex/Claude/Gemini/Copilot startup shapes.scripts/check-coordinator-run.test.mjs(68 lines) — unit coverage for the log checker.Dead-code cleanup
src/lib/terminalDisableStdin.ts+ tests. BothcomputeDisableStdinandshouldForwardTerminalInputhad become constant-wrapping shims after the take/release removal (the function literally wasvoid controlledBy; return false;). Three call sites inTerminalView.tsxsimplified to!taskPtyDetached()/taskPtyDetached().OpenSpec (
openspec/changes/automatic-session-prompt-delivery/)New capability spec
session-prompt-deliveryplusproposal.md,design.md,tasks.md(14/14 tasks done). Validates clean:openspec validate automatic-session-prompt-delivery --strict→Change 'automatic-session-prompt-delivery' is valid.Stats (vs main, includes #138 + #145)
electron/mcp/coordinator.tselectron/mcp/coordinator.test.tsTests
Suite after this PR: 66 test files, 1146 passed / 24 skipped, 0 failed.
Notable additions:
coordinator.test.ts(+1024 / −145): backend-owned initial prompt delivery, prompt-echo suppression during the delivery window, staged notification clearing,signal_done, and a newland_self"stages a landed notification" test.prompt-detect.test.ts(+54 / −9): Codex CLI prompt›, control-character-damaged working status, Gemini CLI input, trust dialogs, startup screens.prompt-control.test.ts(+116 / −8): handoff conditions for each guard, initial-prompt ack scenarios.prompt-autosend-readiness.test.ts(new): startup-blocking detection for Codex loading/booting screens; timeout-abort gates.prompt-draft.test.ts/terminalInputPending.test.ts(new): per-task tracking primitives.tasks.test.ts(+251 / −3) anddesktopNotifications.test.ts: user activity leases, draft persistence, coordinator-aware notification suppression / clearing.coordinator-real-pty.integration.test.ts(new, opt-in): drives a realnode-ptyagainstscripts/fake-agent.mjsforcodex/claude/gemini/copilotprofiles.coordinator-real-agents.integration.test.ts(new, opt-in): hits real installed CLIs when present; coverscodex/claude/gemini.Verification
npm test→ 1146 passed / 24 skippednpm run check(typecheck + lint + prettier) → cleanopenspec validate automatic-session-prompt-delivery --strict→ validgit diff --check→ cleanReviewer guide
Start with the OpenSpec change (
openspec/changes/automatic-session-prompt-delivery/proposal.md→design.md→specs/session-prompt-delivery/spec.md) for the why and intended behavior. Suggested reading order through the diff:electron/mcp/coordinator.ts— backend-owned delivery, queued prompts, landed notifications.electron/mcp/prompt-detect.ts— terminal readiness signals.src/components/autofire-tick.ts— per-tick arbiter with wait reasons.src/components/prompt-control.ts+prompt-autosend-readiness.ts— delivery decision helpers.src/components/PromptInput.tsx,TerminalView.tsx,TaskPanel.tsx— UI wiring and take/release removal.src/store/desktopNotifications.ts— notification suppression.scripts/check-coordinator-run.mjs+ integration tests — verification scaffolding.🤖 Generated with Claude Code