Files
ClaudeDo/docs/superpowers/specs/2026-06-04-refine-task-design.md
mika kuns 0867bc8296 docs(refine): add Refine Task design spec
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 22:53:17 +02:00

6.0 KiB

Refine Task — Design

Date: 2026-06-04 Status: Approved (pending spec review)

Goal

Add a one-click Refine Task action to a task card. Clicking it spawns a headless Claude session that reads the task (and the repo), rewrites the task's description to be clearer and runnable autonomously, and — where it helps — breaks the work into subtasks. The user then reviews/hand-edits the result and queues the task manually.

This is not an interactive terminal session. It is a fire-and-forget headless run, structurally similar to the existing daily-prep ("Prime Claude") flow (PrimeRunner), not the interactive planning flow.

Non-goals / scope

  • No new task status. The task stays Idle throughout; refine only mutates the task's Title/Description and its subtasks.
  • No worktree, no interactive terminal, no auto-queue.
  • No per-task refine config (model, turns) — uses the worker's defaults.
  • Refine does not edit repository files; repo access is read-only.

User flow

  1. User clicks the refine icon on an Idle task's card.
  2. UI calls WorkerHub.RefineTask(taskId)RefineRunner.
  3. RefineRunner spawns claude -p headless in the list's working directory, seeded with a fixed refine prompt + the task's title/description/current subtasks + the task id.
  4. Claude reads the repo (read-only), then calls:
    • mcp__claudedo__update_task to improve title/description, and
    • mcp__claudedo__add_subtask to add steps where useful. Each MCP call broadcasts TaskUpdated, so the description and Steps card update live in the UI.
  5. Run finishes; the card's refine button returns to its idle state. User reviews, optionally hand-edits the description/steps, then queues manually.

Architecture

Worker — RefineRunner

  • New Worker/Refine/RefineRunner.cs implementing IRefineRunner (Worker/Refine/Interfaces/IRefineRunner.cs). Modeled on PrimeRunner.
  • Concurrency / single-flight: an in-flight HashSet<string> of task ids guarded by a lock (or SemaphoreSlim), so the same task cannot refine twice concurrently, but different tasks may refine in parallel. A second click on an already-refining task is a no-op.
  • Guards: only runs when task.Status == Idle. Resolves the list's working directory. If the list has no valid working dir, fall back to a sandbox directory and run text-only (drop Read/Grep/Glob from the allowlist).
  • CLI invocation (relies on the globally-registered claudedo MCP, like daily-prep — no --mcp-config):
    claude -p --output-format stream-json --verbose
           --permission-mode acceptEdits
           --max-turns <N>
           --allowedTools mcp__claudedo__get_task,mcp__claudedo__update_task,mcp__claudedo__add_subtask,Read,Grep,Glob
    
    Edit/Write/Bash are deliberately not whitelisted, so the run is read-only on the repo even under acceptEdits. (Chosen over plan mode to avoid the headless "exit plan mode to act" friction; the allowlist is the real read-only gate.)
  • Logging: stream stdout to a per-run log at logs/refine-<taskId[:8]>.log, truncated at the start of each run.

Prompt — PromptKind.Refine

  • Add Refine to the PromptKind enum in PromptFiles.cs, file prompts/refine.md, with a bundled default.
  • Default prompt instructs: refine one ClaudeDo task so it is ready to run autonomously; ground the description in the actual code (read-only); keep scope tight (no scope creep into adjacent work); add steps as subtasks only when they genuinely help; use only get_task, update_task, add_subtask and the read-only tools; never edit files.
  • Rendered via PromptFiles.Render with {taskId}, {title}, {description}, and the current subtask list seeded into the prompt so the agent knows which steps already exist.

MCP tool — add_subtask

  • New [McpServerTool] on ExternalMcpService (part of the global claudedo MCP), signature add_subtask(taskId, title, orderNum?).
  • Creates a SubtaskEntity via SubtaskRepository; orderNum defaults to append-at-end (max existing + 1). Refuses if the task is Running. Broadcasts TaskUpdated.
  • Append semantics, not replace: the current subtasks are already in the prompt, so the agent only adds missing steps; re-running refine will not silently wipe steps the user hand-edited.
  • update_task already exists (title/description/commitType) and is reused unchanged.

UI — button, icon, feedback

  • Icon: add the supplied SVG as an Icon.Refine StreamGeometry in IslandStyles.axaml, rendered as a stroked Path (plan-icon style, fill none) — it is line art, so per the PathIcon-fills-geometry gotcha it must be stroked, not filled.
  • Button: a new icon-btn in TaskRowView.axaml near the star button, visible only when the task is Idle. Bound to a new RefineTaskCommand on TasksIslandViewModel.
  • Feedback: new broadcaster events RefineStarted(taskId) / RefineFinished(taskId, ok, error?) drive an IsRefining flag on TaskRowViewModel; the button shows a busy/disabled state while running. The description and Steps card update live via the existing TaskUpdated events fired by the MCP calls.
  • Wire RefineTask through IWorkerClient / WorkerClient, the WorkerHub method, and update the hand-rolled test fakes in both test projects.

Testing

  • add_subtask: creates the row, appends order correctly, refuses when Running, broadcasts TaskUpdated.
  • Refine prompt builder and CLI-args builder produce the expected prompt/flags (including the text-only fallback when no working dir).
  • RefineRunner guards: Idle-only, per-task single-flight no-op on a second concurrent call.
  • No test spawns the real claude CLI (project rule). The end-to-end run is a manual smoke step.

Open implementation calls (decided)

  • Permission mode: acceptEdits + restricted allowlist for read-only (rather than plan mode).
  • add_subtask: append-only (rather than replace-all).