6.0 KiB
6.0 KiB
Planning UX Polish + Sequential Subtask Queue
Status: design Date: 2026-04-24 Scope: three small UX changes + one feature — sequential execution of planning subtasks triggered from the context menu.
Goals
- Collapse the children of a finished planning-parent row in the task list by default.
- Allow the user to collapse the Description section in the Details pane.
- Halve the width of the GridSplitters between islands.
- Let the user queue all subtasks of a planning parent so they run one after another, with a new
Waitingstatus for pending siblings.
1. Auto-collapse done planning parents
Rule for "done": a planning parent is "done" when every one of its children has Status == Done.
Changes:
TaskRowViewModel: add UI-only[ObservableProperty] bool _areChildrenExpanded. Default computed from status —falsewhen the row is a done planning parent, elsetrue. Not persisted.- Add
[RelayCommand] void ToggleChildrenExpanded(). TasksIslandView.axaml(orTaskRowView.axaml): chevron button on the planning-parent row, visible only whenIsPlanningParent && HasPlanningChildren. Bound to the toggle command.TasksIslandViewModel.Regroup(): before adding child rows toOpenItems/CompletedItems, check each child's parent row inItems. If the parent'sAreChildrenExpanded == false, skip the child.- When a planning parent flips from "not done" → "done" in
OnWorkerTaskUpdated, callRegroup()so the collapse takes effect.
No DB changes.
2. Collapsible description in Details pane
Changes:
DetailsIslandViewModel:[ObservableProperty] bool _isDescriptionExpanded = true+[RelayCommand] void ToggleDescriptionExpanded().DetailsIslandView.axaml: wrap the existing descriptionTextBoxin aStackPanel; add a thin header row with the label "Description" and a chevron button. Body'sIsVisiblebinds to the flag.- State is per ViewModel instance — reset to
truewhenever a different task is loaded.
No persistence.
3. Narrower GridSplitters
MainWindow.axaml lines 158 and 170: Width="5" → Width="3" on both GridSplitter elements.
That's the whole change.
4. Sequential subtask queue
Data
ClaudeDo.Data/Models/TaskStatus.cs: add a new enum valueWaiting(lowercase serialized formwaiting, matching existing convention).- Verify status is stored as string (it should be based on existing patterns). If stored as int, ensure new value gets a stable numeric slot at the end of the enum to avoid breaking existing rows. No EF migration beyond what the enum emits automatically.
Worker
- New SignalR hub method:
QueuePlanningSubtasksAsync(string parentTaskId) : Task.- Loads all children of the parent, ordered by
SortOrder. - Validates: parent must be a planning parent, children must currently all be in
ManualorPlanned(reject if any child is already Queued/Running/Done/Failed, surface a friendly error). - First child →
Queued. All other children →Waiting. Save. - Emit
TaskUpdatedfor each affected task.
- Loads all children of the parent, ordered by
- Chain progression — hook into the existing finish/complete path that already fires
TaskFinished:- On a child task finishing with status
Doneand its parent has waiting siblings: find the next sibling by(ParentTaskId == parent.Id && Status == Waiting)ordered bySortOrder, flip toQueued, emitTaskUpdated, and let the existing queue pickup loop pick it up. - On
Failed: do nothing. RemainingWaitingsiblings stay waiting. (A toast for failed tasks will be added in a later spec.)
- On a child task finishing with status
This logic lives in a new PlanningChainCoordinator service (or similar) in ClaudeDo.Worker/Planning/, registered as a singleton and wired into whatever already emits task-finished events.
UI
TaskRowView— add context menu entry "Queue subtasks sequentially":IsVisiblebound toIsPlanningParent && HasPlanningChildren.IsEnabledwhen all children are inManual/Plannedstate (new property onTaskRowViewModel:CanQueueSubtasksSequentially).- Calls
WorkerClient.QueuePlanningSubtasksAsync(Id).
TaskRowViewModel:- Add
IsWaiting => Status == TaskStatus.Waitingand extendStatusChipClassswitch to return a new class"waiting". - Add
CanQueueSubtasksSequentially(computed; requires access to children).
- Add
StatusColorConverter— add a muted color forWaiting(proposed: the existingTextMuteBrushor a faint cyan).- Task list — planning parent continues to appear in virtual:queued because it has a
Queuedchild (existing logic). Extend the virtual:queued match predicate inTasksIslandViewModel.TaskMatchesListso a task matches whenStatus == Queued || Status == Waiting. This ensures all sibling subtasks (the queued one + the waiting ones) render under the parent in that list.
Client
IWorkerClient/WorkerClient: addQueuePlanningSubtasksAsync(string parentTaskId)that calls the hub method.
Out of scope
- Toast notifications on subtask failure (separate follow-up spec).
- Retrying a stopped chain from a failed task (user does it manually via existing actions).
- Persisting the collapse state of planning parents or the Description across sessions.
- Drag-to-reorder of waiting subtasks (execution order =
SortOrderat the moment the chain starts).
Validation plan
Manual:
- Plan a task with 3 subtasks. Context-menu → Queue subtasks sequentially. Confirm first = Queued, others = Waiting. Watch the first run to Done, confirm the second flips Queued → Running automatically.
- Force-fail subtask 2 (cancel or make it fail). Confirm subtask 3 stays Waiting; no further dispatch.
- Once all three are Done, confirm the planning parent auto-collapses in the list.
- Toggle the Description chevron in the Details pane on an arbitrary task.
- Eyeball the narrower GridSplitter — still resizable, still hittable.
Automated (minimal — only where cheap):
- Worker-level unit test for
PlanningChainCoordinator: happy-path chain advance on Done; no advance on Failed; correct ordering bySortOrder.