Files
ClaudeDo/docs/superpowers/specs/2026-06-25-interactive-ask-user-design.md

5.3 KiB

Interactive "Answer Claude's Questions" — Design

Date: 2026-06-25 Status: Approved (brainstormed with Mika)

Goal

Let the user answer a question Claude raises mid-run from inside Mission Control, without leaving the autonomous-execution model. Not a chat panel, not a terminal, not proactive steering — only: Claude surfaces a question → the user types an answer → the run continues with that answer in context.

User decisions (brainstorm):

  • Scope: "I mostly want to answer his questions if he surfaces any."
  • Trigger: any running task may ask, with a 3-minute answer window.

Why not the alternatives

  • Embedded terminal / PTY — would destroy the NDJSON contract the whole worker pipeline depends on (StreamAnalyzer, token accounting, auto-commit, status flow) and needs a terminal-emulator control Avalonia doesn't have. Rejected.
  • Streaming-stdin (--input-format stream-json) — right tool for a free-form chat, overkill here. Rejected for v1.
  • --resume per-turn — already exists; not live (cold process per turn).

Mechanism

The in-task MCP already blocks the claude -p process while a tool call is in flight. That blocking is the pause. Add one in-task MCP tool, AskUser(question):

  1. The tool resolves the caller task id, registers a pending question + a TaskCompletionSource<string> in a singleton PendingQuestionRegistry, and broadcasts TaskQuestionAsked(taskId, questionId, question).
  2. Mission Control surfaces the question with an input box.
  3. The user answers → WorkerHub.AnswerTaskQuestion resolves the TCS → the tool returns the answer as its result → Claude continues.
  4. No answer within 3 minutes → the tool returns "No response received within 3 minutes — proceed using your best judgment." and the run carries on autonomously.

Key facts that make this work

  • No persisted status change. The task is still genuinely Running (process alive, blocked mid-tool-call). "Waiting for input" is ephemeral: in-memory registry + live SignalR events + a UI overlay. No TaskStatus enum value, no TaskStateService transition, no EF migration. If the worker dies mid-wait, StaleTaskRecovery flips the orphaned Running row to Failed like any interrupted run.
  • MCP_TOOL_TIMEOUT must be raised. Claude Code caps HTTP MCP tool calls at 60 s by default. The claudedo_run MCP is HTTP, so ClaudeProcess must set MCP_TOOL_TIMEOUT=200000 (≈3 min + margin) on the spawned process or the 3-min window is silently truncated to 60 s.
  • MCP wired for all runs. Today TaskRunner only mints the run MCP for standalone top-level tasks (for SuggestImprovement). To satisfy "any running task," move the MCP-identity setup out of that gate so every RunAsync gets claudedo_run. AllowedTools always includes mcp__claudedo_run__AskUser; SuggestImprovement stays gated to improvement-eligible (standalone) runs.

Surface changes

Worker (mostly new files):

  • Runner/PendingQuestionRegistry.cs (new, singleton) — Register, TryAnswer, Get, Remove; one pending question per task.
  • Runner/TaskRunMcpService.cs (edit) — add AskUser [McpServerTool]; inject the registry.
  • Runner/TaskRunner.cs (edit) — wire MCP identity for all runs; add AskUser to allowed tools.
  • Runner/ClaudeProcess.cs (edit) — set MCP_TOOL_TIMEOUT env.
  • Hub/HubBroadcaster.cs (edit) — TaskQuestionAsked, TaskQuestionResolved.
  • Hub/WorkerHub.cs (edit) — AnswerTaskQuestion, GetPendingQuestion + DTO.
  • Program.cs (edit) — register PendingQuestionRegistry singleton.
  • System prompt (edit) — one line telling Claude the tool exists and to use it only when a wrong guess would be costly/irreversible (otherwise proceed).

UI:

  • Services/IWorkerClient.cs + WorkerClient.cs (edit) — AnswerTaskQuestionAsync, GetPendingQuestionAsync, TaskQuestionAskedEvent, TaskQuestionResolvedEvent.
  • ViewModels/Islands/TaskMonitorViewModel.cs (edit, hot file) — pending-question state, AnswerDraft, SubmitAnswerCommand, clear on finish/resolve.
  • ViewModels/MissionControlViewModel.cs (edit) — hydrate pending question on attach.
  • Views/MissionControl/MonitorPaneView.axaml (edit, hot file) — additive question/answer banner above the terminal.
  • Localization/locales/en.json + de.jsonmissionControl.question.* keys.

Tests: PendingQuestionRegistry (answer/timeout/unknown/overwrite), AskUser tool (answer + timeout fallback, fake broadcaster — no real Claude), TaskMonitorViewModel (surface/submit/clear). Update IWorkerClient fakes in both test projects.

Concurrency note

Two files (TaskMonitorViewModel.cs, MonitorPaneView.axaml) are also being touched by a concurrent Mission Control drag-and-drop session on the shared main tree. Keep edits additive, commit explicit paths only (never git add -A).

Verification gaps (manual)

  1. Real-Claude smoke test — confirm a blocking AskUser call survives ≥3 min with MCP_TOOL_TIMEOUT=200000 and that the model actually calls the tool when uncertain.
  2. Visual — the question banner + input box in the pane (Mika does the visual pass).

Non-goals

Free-form chat panel; proactive steering; tool-permission prompts (stays auto); ContinueAsync/resumed runs gaining AskUser (deferred follow-up).