TryInvokeAsync catches every exception and returns null, which is fine
for read-only calls but hid real HubException reasons behind a generic
"offline" message for the 7 mutating call sites (RestoreDefaultAgents,
UpsertPrimeSchedule, AddDailyNote, CleanupFinishedWorktrees,
ResetAllWorktrees, ForceRemoveWorktree, BuildPlanningIntegrationBranch)
— the same bug class fixed for ApproveReview in e1807fd. Each of the 22
TryInvokeAsync call sites was audited; the 15 read-only ones are left
unchanged (empty/offline is the right display). For the 7 switched to
a direct hub invoke, every caller was checked and, where it had no
catch, one was added so the exception surfaces (StatusMessage,
ShowErrorAsync/CombinedWarning) instead of crashing.
## Problem
Nach "Neuen Task hinzufügen" (Eingabefeld im Tasks-Header, `Ctrl+N`) wird der Task angelegt und in die
Liste eingefügt, aber **nicht ausgewählt**. Um Beschreibung/Config zu ergänzen, muss man ihn erst
manuell anklicken.
## Ist-Zustand
`src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs`, `AddAsync()` (ab Zeile ~424):
legt die `TaskEntity` an, speichert über `TaskRepository.AddA
ClaudeDo-Task: 5af6ac9acb4447929e3e550c3c233a25
Reminders written down as todos had no home: every task looked like Claude work.
A manual task now shows a MANUAL badge and hides send-to-queue, refine and the
planning session; the queue picker, daily prep and the list handler all skip it,
with a TaskStateService guard so the MCP surface and hub cannot start one either.
Opening a hand-driven ConPTY session stays available on purpose.
A list can be marked manual in its settings, which makes tasks created there
(UI and MCP add_task) start out manual. Toggle per task from its context menu.
A task driven by hand in Mission Control is Idle with an Active worktree, so its
lifecycle chip read "Parked" — indistinguishable from a task genuinely set aside.
The shell now mirrors Mission Control's open panes onto the rows, which show an
accent "Interactive" chip instead; tapping it surfaces Mission Control and
focuses that pane (the open command already dedupes by task id).
EditableTitle had no save handler at all (only EditableDescription did), so
retitling a task in the details island was silently discarded on rebind. Mirror
the debounced description save, capturing the row so a task switch mid-debounce
cannot retitle the wrong task.
An embedded ConPTY session leaves its worktree changed but never touches
task status, so hand-driven work had no path into the review/merge flow.
Add SubmitTaskForReview: commit the worktree (same auto-commit as a headless
run), then transition Idle/Failed -> WaitingForReview via the new
TaskStateService.SubmitInteractiveForReviewAsync. Approve then merges it.
Surfaces: a 'Submit for review' button in the detail work console (shown for
an Idle/Failed task with a worktree) and on the ConPTY Command Center pane
header (task-based panes; closes the pane on success). Tests cover the new
transition (Idle/Failed accepted, Running/Queued/Done/Review rejected).
Approve & Merge is disabled until the pending changes have been inspected:
DetailsIslandViewModel tracks ReviewDiffViewed (reset on task switch and on
every state change), MergeSectionViewModel raises DiffViewed when a diff or
combined diff is opened and exposes HasReviewableDiff, and a hint sits next
to the button. A review with nothing to inspect (sandbox run, no worktree)
approves straight through.
ClaudeDo-Task: f9809a93
Planning start/resume opened an external Windows Terminal (wt) window.
Route them through the embedded ConPTY Command Center pane instead, matching
the existing interactive-session UX (no external window).
- Extract bare planning arg builders (BuildPlanningStartArgs/ResumeArgs) from
WindowsTerminalLauncher; the wt path still uses them (kept, not removed).
- InteractiveLaunchSpecService.BuildPlanningStart/Resume map a planning
context into a LaunchSpec (planning args + env: MAX_THINKING_TOKENS,
CLAUDEDO_PLANNING_TOKEN). Hub GetPlanningStart/ResumeLaunchSpec run
StartAsync/ResumeAsync then return the spec.
- UI: OpenPlanningSession + the resume branch raise OpenPlanningConPtyRequested;
the shell opens Mission Control and hosts a planning ConPTY pane.
Env is process-global by design (sequential human-paced sessions). wt planning
code retained. Tests added for the arg/env mapping.
A planning parent stays Status=Idle while its session is Active (or
Finalized-but-not-queued), so the lifecycle chip read "Idle"/"Parked"
next to the PLANNING/PLANNED badge. Hide the chip in that case; the badge
already conveys the state.
A --json-schema run can echo the structured {"summary":...} JSON as the
result text, which then landed verbatim in task.Result and rendered raw in
the OUTCOME card. Unwrap the summary in StreamAnalyzer when the result text
is itself such a JSON object (a plain markdown result is kept as-is), and
add a UI safety net in ApplyOutcome for rows already persisted as raw JSON.
Finalize/Discard broadcast only the parent's TaskUpdated; the delta path
updated the parent row but never recomputed child-derived flags or dropped
discarded children, so subtasks stayed "Draft" after finalize and deleted
rows lingered after discard until a manual reload. Reconcile the whole
list when the updated task is (or owns) a planning subtree.
Opening the dropped IStorageFile stream ran outside the try, so a
first-drop failure escaped the async-void handler as an unobserved fault
(generic "An error occurred"). Wrap the stream-open loop and surface the
exception type via DropStatus; include the exception type in the generic
add-file failure too, so the intermittent case is analyzable.
ResumePlanningSessionAsync wrapped everything in an empty catch, so a
failed resume vanished silently. Report it via the footer error channel
with a dedicated planningResumeFailed message.
The embedded ConPTY terminal replaced the in-app streaming interactive session, so
delete the dead stack: StreamingClaudeSession, InteractiveSessionService,
ProcessClaudeStreamTransport, IClaudeStreamTransport, ILiveSession, LiveSessionRegistry,
IdleSessionReaper (+ WorkerConfig.InteractiveIdleTimeoutMinutes), the WorkerHub
interactive methods + HubBroadcaster events, IWorkerClient interactive members, the
TaskMonitorViewModel composer + SessionTerminalView composer markup, and the old
'Run interactively' entry. AskUser/PendingQuestionRegistry, the autonomous path,
planning, ResumeTaskInTerminal, and all ConPTY code are kept. Localization pruned.
Adds an 'Open ConPTY session' entry that fetches a task's launch spec and hosts
an embedded ConPTY terminal as a Mission Control pane, coexisting with the
streamed-log monitor panes (streaming stack untouched). Introduces IMissionControlPane
+ ConPtyPaneViewModel, a non-destructive Panes mirror (Monitors prefix + ConPtySessions
suffix) so unrelated monitor churn never tears down a live terminal, and a grid<->tabs
layout toggle. Launch failures surface via the footer error strip.
Queued rows are now QueuedMessageViewModel (Text + RemoveCommand); each shows a
✕ (Icon.WinClose) that calls RemoveQueuedInteractiveMessageAsync(taskId, text).
The worker re-broadcasts the queue, rebuilding the strip without the removed
message. Adds session.composer.unqueue (en/de).
A queued message now appears in a pending strip above the input box (driven by
InteractiveQueueChanged), not optimistically in the transcript. The transcript
user line is added on delivery via InteractiveMessageSent. SessionTerminalView
gains QueuedMessages/HasQueuedMessages styled props (Mission Control); WorkConsole
binds Monitor.* (task detail). Adds session.composer.queued (en/de).
LogKindForegroundConverter drives the log message foreground via a local
binding (beats the dim local value), so user messages render in the accent
color instead of vanishing into the transcript. Adds a small stop (Icon.Stop)
button next to Send in both composers (SessionTerminalView + WorkConsole) wired
to InterruptInteractiveCommand → InterruptInteractiveSessionAsync. Adds
session.composer.interrupt (en/de).
TaskMonitorViewModel gains IsInteractiveLive + ComposerDraft + SubmitComposer
(optimistic LogKind.User echo, then SendInteractiveMessageAsync) + StopInteractive,
driven by the InteractiveSessionStarted/Ended events. Since DetailsIslandViewModel
embeds this monitor, both task detail and Mission Control get the composer. Mission
Control auto-creates a monitor on InteractiveSessionStarted. Adds LogKind.User.
TaskMonitorViewModel surfaces a pending AskUser question (TaskQuestionAsked /
TaskQuestionResolved events) with an AnswerDraft + SubmitAnswerCommand that calls
the new IWorkerClient.AnswerTaskQuestionAsync; MonitorPaneView shows an accent
question banner with an input box above the terminal. Pending question is cleared
on answer/resolve/finish and re-hydrated on attach via GetPendingQuestionAsync.
en/de localization for missionControl.question.*; test fakes updated.
Add the borderless, transparent, topmost, click-through DragGhostWindow that
hosts a tilted (~-6deg) translucent snapshot of the dragged row, a
TaskDragController that owns its lifecycle (snapshot -> show -> follow -> close),
and a pure DPI-aware DragHitTest helper (unit-tested) for the cross-window
screen hit test. Adds the TaskRowViewModel.IsDragging flag and the
'grabbed' Border.task-row.dragging style (lift + scale + lower opacity +
shadow). Not yet wired into the drag source.
Replace the static DETAILS label and its dead space with a segment switcher; the card body now shows one section at a time. Step/file counts sit in the tab labels, the edit/preview toggle is scoped to Description, and drag-and-drop or add jumps to the Files tab. Tab labels localized (en/de).
Drop a file anywhere on the detail pane to attach it: pane-wide drop target
with a 'Drop to attach' hover overlay (Copy cursor, gated on an idle selected
task), an explicit lingering confirmation/error line, plus an Attachments list
with size, remove, and an Add file… picker in the DETAILS card. ComposedPreview
now shows the reference files too. en/de keys added.
The RequestConflictResolution Func was declared on 5 VMs and hand-threaded shell->details->merge-section->diff->merge-modal. Replaced with a DI-singleton IMergeCoordinator (MergeCoordinator holder; shell wires its Handler at composition, breaking the shell<->island cycle). Invokers (MergeModal, DetailsIsland, WorktreesOverview) depend on the interface; the two pass-through VMs (DiffModal, MergeSection) drop the seam entirely. No behavior change; conflict-seam + batch tests rewired to assert via the coordinator.
Collapses the ~10 per-modal Show*Modal Func callbacks (wired separately on the shell and the lists island) into one IDialogService + WindowDialogService impl. Removes the RepoImport/WorktreesOverview dialog construction duplicated across MainWindow and ListsIslandView, plus the Confirm/Error dialogs duplicated in both code-behinds. Shell/lists Open* commands now route through an injected Dialogs handle (propagated shell->lists); the per-list worktrees overview also wires conflict resolution now, matching the global one. No VM ctor changes (Dialogs is a settable seam), so no test-fake impact.
Drops the append-SortOrder query duplicated inline in TasksIslandViewModel.AddAsync; the repository (already used by MCP AddTask) is now the single home for the create+SortOrder invariant. Sets Status=Idle explicitly for parity.
- The Merge button in the Diff window now hands a conflicting merge to the in-app
3-pane editor (MergeModal routes 'conflict' through RequestConflictResolution,
the same seam Approve uses) instead of dead-ending on a conflict message.
- Add a conflict overview ruler right of the Result pane: a proportional map of
every conflict in the file, recolored by resolved state, click a tick to jump —
so conflicts are findable in long files without scrolling.
- New MergeResolvedEdgeBrush token + conflictMap en/de key. Ui 128 + Loc 16 green.
- Feedback box + a new "Resume session" button move from the Git tab to the
Output tab; the Git review block keeps Approve & Merge / Park / Cancel / Reset.
- Add a "Parked" chip for Idle tasks that still hold an Active worktree.
- Stop showing the "Session was Cancelled" band on cancel (failed-only now).
- Fix the Worktrees-overview state-chip contrast (dark text on the colour).
Grow the detail-pane Git tab into the review+merge cockpit: target,
pre-flight mergeability, inspect actions, then the four review verbs
(Approve & Merge / Send back / Park / Cancel) plus a demoted
Reset (discard branch).
The decision block is gated independently of the merge controls so
sandbox (no-worktree) review tasks still get the buttons.
- Add ParkReviewCommand (-> RejectReviewToIdleAsync)
- Send back (reject-to-queue) disabled until feedback is entered
- Remove the mislabeled [Continue]/[Reset] line from the Output tab
- Accent dot on the Git tab while awaiting review
Kontext: src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs ist mit 1431 Zeilen ein God-VM mit ~12 Concerns (Log-Streaming, Titel/Description-Editing, Subtasks, Child-Outcomes, Merge-Preview/-Targets, Diff, Agent-Settings-Overrides, Notes-Mode, Prep-Mode, Tabs, Session-Outcome/Roadblocks, Worktree-Info). Jedes neue Feature landet dort.
Änderungen — drei klar abgrenzbare Sektionen als ei
ClaudeDo-Task: 483e419f-1ec8-46ba-986b-8b90d6596b49
Add 16 missing members to IWorkerClient (IsReconnecting, WorkerLogReceivedEvent,
PrimeFired, LastApproveTarget, Refresh/RestoreDefaultAgents, UpdateAppSettings,
prime schedule CRUD, UpdateList/UpdateListConfig, all worktree ops).
Switch all production consumers off the concrete WorkerClient type; only
Program.cs/App host still resolves the concrete registration.
Update StubWorkerClient and FakeWorkerClient to satisfy the expanded interface.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>