4.4 KiB
4.4 KiB
Plan — Interactive "Answer Claude's Questions"
Spec: docs/superpowers/specs/2026-06-25-interactive-ask-user-design.md
Implement on the shared main tree. Commit explicit paths per task (never git add -A).
Build with -c Release (running Worker locks Debug). No real-Claude tests.
Task 1 — PendingQuestionRegistry (worker, new file)
src/ClaudeDo.Worker/Runner/PendingQuestionRegistry.cs: singleton;record PendingQuestion(TaskId, QuestionId, Question).(string QuestionId, Task<string> Answer) Register(taskId, question)— overwrites any stale entry,RunContinuationsAsynchronously.bool TryAnswer(taskId, questionId, answer);PendingQuestion? Get(taskId);void Remove(taskId, questionId).
- Test:
tests/ClaudeDo.Worker.Tests/Runner/PendingQuestionRegistryTests.cs— register→answer resolves the task; wrong questionId no-ops; Get reflects state; second Register overwrites.
Task 2 — AskUser MCP tool (worker)
TaskRunMcpService.cs: injectPendingQuestionRegistry; add[McpServerTool] async Task<string> AskUser(string question, CancellationToken ct):- caller id from
_ctx.Current.CallerTaskId; register; broadcastTaskQuestionAsked. - await answer via
Task<string>.WaitAsyncwith a 3-min linked-CTS; on timeout return the fallback string; on request-cancel rethrow. finally:Remove+ broadcastTaskQuestionResolved.[Description]: when to use (only when a wrong guess is costly/irreversible; otherwise proceed).
- caller id from
- Test:
tests/ClaudeDo.Worker.Tests/Runner/AskUserToolTests.cs— answer path returns the answer; timeout path returns fallback (inject a short timeout or a seam) with a fake broadcaster + stub context accessor.
Task 3 — Wire MCP for all runs + timeout env (worker)
TaskRunner.RunAsync: move MCP-identity setup out of thestandalonegate so every run getsclaudedo_run;AllowedTools=mcp__claudedo_run__AskUseralways, append,mcp__claudedo_run__SuggestImprovementwhen standalone. Keep token cleanup infinally.ClaudeProcess.cs:psi.Environment["MCP_TOOL_TIMEOUT"] = "200000";.- System prompt file (PromptKind.System default): add one guidance line about
AskUser.
Task 4 — Hub + Broadcaster (worker)
HubBroadcaster.cs:TaskQuestionAsked(taskId, questionId, question),TaskQuestionResolved(taskId, questionId).WorkerHub.cs: inject registry;bool AnswerTaskQuestion(taskId, questionId, answer);PendingQuestionDto? GetPendingQuestion(taskId);record PendingQuestionDto(...).Program.cs: registerPendingQuestionRegistryas singleton.
Task 5 — UI client (IWorkerClient/WorkerClient + fakes)
IWorkerClient:Task AnswerTaskQuestionAsync(taskId, questionId, answer),Task<PendingQuestionDto?> GetPendingQuestionAsync(taskId), eventsAction<string,string,string>? TaskQuestionAskedEvent,Action<string,string>? TaskQuestionResolvedEvent; UI DTO record.WorkerClient: implement invokes +On<...>handlers raising the events.- Update hand-rolled
IWorkerClientfakes in Ui.Tests (and Worker.Tests if present).
Task 6 — TaskMonitorViewModel (hot file)
- Subscribe both events (filter by
_subscribedTaskId); dispose handlers. - Props:
PendingQuestionId,PendingQuestion,HasPendingQuestion,AnswerDraft,IsWaitingForInput. SubmitAnswerCommand(CanExecute: non-empty draft + HasPendingQuestion) →AnswerTaskQuestionAsync; clear draft.- Clear pending on
TaskFinishedfor this task and inReset(). - Test:
TaskMonitorViewModelTests— asked event surfaces question; submit invokes client + clears; resolved/finished clears.
Task 7 — Hydrate on attach (MissionControlViewModel)
- In
HydrateAsync, afterApplyState, callGetPendingQuestionAsync(taskId); if present, set the monitor's pending question (re-attach case).
Task 8 — View banner (hot file, additive)
MonitorPaneView.axaml: aBorder DockPanel.Dock="Top"aboveSessionTerminalView,IsVisible="{Binding HasPendingQuestion}", showing the question text, aTextBoxbound toAnswerDraft(Enter submits), and a SendButton→SubmitAnswerCommand. Mirror the roadblock-banner styling.
Task 9 — Localization
en.json+de.json:missionControl.question.title,.placeholder,.send. Keep parity (Localization.Tests).
Task 10 — Build + test + verify
dotnet buildApp + Worker-c Release; run Worker.Tests, Ui.Tests, Localization.Tests.- Self-review diffs. Flag the two manual verification gaps to Mika. Do not push.