Submit for Review is now disabled while a ConPTY pane is starting, has
failed to launch, or has already exited, and MissionControlViewModel
guards against a rapid double-click racing two SubmitTaskForReviewAsync
calls. A failed launch no longer permanently occupies its TaskId dedupe
slot -- a Retry button re-fetches the launch spec and restarts the pane
in place. CloseConPtySession/Dispose now also unsubscribe
SubmitForReviewRequested, matching the other pane event handlers. Also
fixes the pre-existing nullable-dereference warning in
IslandsShellViewModel.SyncInteractiveSessionChips.
TaskStateService.CancelAsync allowed cancelling a WaitingForReview task
even while PlanningMergeOrchestrator was mid-drain on it: ApproveReview
awaits the whole multi-subtask merge synchronously, so a concurrent
CancelReview (UI or MCP) could flip the parent to Cancelled while the
orchestrator kept merging children onto the target branch, then
FinalizeParentDoneAsync would find the parent no longer WaitingForReview
and give up - leaving the merged diffs stranded with no rollback.
CancelAsync now rejects with a clear reason when HasActiveMerge(taskId)
is true. TaskStateService can't take PlanningMergeOrchestrator as a
direct constructor dependency (circular back to ITaskStateService), so
it takes a lazily-resolved Func<IActiveMergeState> instead, mirroring
the existing Func<ITaskStateService> cycle-break already used for
PlanningChainCoordinator.
UI polish: DetailsIslandViewModel.IsMergeDraining gates
CancelReviewCommand's CanExecute (same shape as
WorktreesOverviewModalViewModel.IsMerging), and the command's catch now
raises ErrorReported instead of swallowing the rejection silently.
Bumps the verified-against commit to 774f9d3 (the RunNow/picker
double-dispatch race fix) and documents the new claim-before-create
ordering and the RunCancellationRegistry double-registration guard.
Bump conpty-sessions.md's verified-against commit to d84607f and add a
section covering the CanSendToQueue / EnqueueTaskAsync interactive-session
gate and the switch to routing through the worker hub.
Deleting a child from the details pane hard-deleted straight from the UI
process via TaskRepository, bypassing TaskStateService.TryAdvanceParentAsync
entirely. Deleting the last child of a WaitingForChildren parent that way
left it wedged forever. WorkerHub.DeleteTask now mirrors the MCP delete_task
tool (running-task guard, FK-friendly error, advance-parent call), and the
UI goes through it.
TryAdvanceParentAsync also short-circuited when zero children remained,
treating "no children left" as "nothing to evaluate" instead of "all done" -
removed the early return so an empty child list (vacuously) counts as all
terminal.
review_task/continue_merge on a planning parent always leaves conflicts in
the tree, and the UI auto-opened the resolver on every PlanningMergeConflict
broadcast regardless of who started the merge -- so a running Claude session
resolving a unit-merge conflict could race a human editing the same shared
checkout in a resolver window neither of them asked for.
PlanningMergeOrchestrator.StartAsync now takes an externallyDriven flag (set
by ExternalMcpService's MCP-driven review_task path, left false for the UI's
ApproveReview) that rides along on the PlanningMergeConflict broadcast. The
UI only auto-opens the resolver when it's false; otherwise it shows a
persistent banner with a manual "Open resolver" button, cleared on
PlanningMergeAborted/PlanningCompleted. A new GetActiveExternalConflictsAsync
query (checked against GitService.IsMidMergeAsync rather than the in-memory
flag alone) lets the UI resync the banner on reconnect instead of trusting a
one-shot broadcast that isn't replayed after a restart.
The childless single-task conflict path was checked and needed no change --
it only broadcasts the generic TaskUpdated, never PlanningMergeConflict.
preview_merge/preview_merge_set now report isEmpty (ahead==0, or
HandlerBaseCommit==HandlerHeadCommit for a worktree-less list-handler
task) instead of leaving an empty branch indistinguishable from a small
one. preview_merge also stops throwing for worktree-less handler tasks,
falling back to their fixed commit range. review_task's parent approve
returns emptyChildren, naming the Done children whose review range
contributed nothing before the unit merge lands. TaskRefDto/TaskDto now
expose roadblockCount so a CLAUDEDO_BLOCKED child is identifiable over
MCP, since it still reaches Done per the unified parent model.
WaitingForChildren already counted as "changed" since it's outside Queued/Running,
so waiting on a planning parent returned immediately even though its children were
still running. The new opt-in flag (default false, unchanged behavior) keeps polling
through WaitingForChildren and only reports changed once the parent reaches
WaitingForReview or a terminal status.
list_tasks on a list of ~100 verbosely-described tasks could return
390k+ characters in one call, blowing past the caller's token limit.
Both tools now default to lean TaskRefDto references (no
Description/Result) and take an includeDescription flag to opt back
into the full TaskDto payload — same flag-alongside-nullable-payload
idiom already used by BatchGetTaskResult/TaskConfigResult. get_task is
unchanged.
add_task and batch_add_tasks now report up to 3 open (non-terminal)
tasks in the same list with a strongly overlapping title, so a
parallel agent can notice and mention a likely duplicate instead of
silently creating one. The task is always created regardless. Uses a
cheap normalized-word overlap heuristic (no embeddings/LLM call),
robust to German umlaut/digraph spelling variants. Breaking change:
AddTask now returns AddTaskResult { task, possibleDuplicates } instead
of a bare TaskRefDto; BatchAddTaskResult gained a PossibleDuplicates
field.
AgentStripView was superseded by WorkConsole during the task-detail
redesign but never wired into any view and never deleted. Drops the
unused Border.agent-strip / diff-meter styles and the 14 agent.* locale
keys that only it referenced, and marks A4 in the feature-unification
spec as resolved by deletion.
List settings were reachable via three doors (header button, context
menu, double-click). Drop the context menu entry, matching the same
cut already made for the "Let Claude handle it" entry point.
review-merge: worktree-less approvals run the gate too, and all three UI merge
entry points handle verify_failed explicitly.
Ui gotchas: NumericUpDown.Value is decimal? and nulls while the box is empty.
A task selected before its run started kept LatestRunSessionId null, so the
roadblock reply box and Continue stayed dead until it was re-selected.
The handoff left the Phase 1-2 tile open so its last message could be read, but
its process is gone by then and the terminal renders empty -- a dead
placeholder. It is closed on handoff now.
Also drops the WARN flood from worktree cleanup (already-unregistered worktree
and already-deleted branch are normal outcomes, not failures) and replaces the
fixed sleep in UsageGate_TransitionLogging_FiresOncePerChange with the polling
helper that already sits three lines below it in the same file.
The app is dark-only (App.axaml pins RequestedThemeVariant="Dark", Tokens.axaml
has no light variant), so every "Dark/Light" line was unverifiable by
construction. Adds the roadblock-reply session-id finding from the same pass.
Verified: max-turns hints, usage pill/modal, list-handler run end to end.
New findings: NumericUpDown null binding throws in the settings modal, the
verify gate is skipped for worktree-less tasks and unhandled in two of three
merge entry points, the handoff leaves an empty pane, worktree cleanup logs
already-done as WARN, orphaned git worktrees are invisible to the app, modal
bodies are clipped at the bottom, usage tables need formatting work.
Ten of the twelve listed bugs/nits are fixed in the code (outcome JSON,
subtask terminology, live child rows, AskUser in the detail island, Icon.Plus,
gear glyph, rename display, TurnsText, conflict Continue gate, ConPTY resume)
and the blocked-approve silent fail is handled by the WorkerHub throw. Adds the
open verification block for the planning-resume fix.
Der Button, der aus „Problem erkannt" ein „Problem gelöst" macht: startet eine interaktive Claude-Session, die dem Nutzer beim Einrichten hilft.
## Warum externes Terminal
Der ConPTY-Stack (`PtyTerminalSession`, `ConPtyPaneView`) liegt in `ClaudeDo.Ui` und ist Avalonia — der Installer ist WPF und referenziert nur Data/Releases/Localization. Beim Fresh Install sind `app\`/`worker\` außerdem noch n
ClaudeDo-Task: 4e196058-38a3-404f-9862-4cb0e90195da
Re-runs the environment checks against the installed configuration
(worker.config.json + detected install dir) without blocking navigation
and without auto-running on window open, only on a "Recheck" click.
Extracted the check-row rendering and check-run logic (busy state,
summary, Recheck command) out of SystemCheckPage into a shared
Checks/CheckListViewModel + Checks/CheckListView, composed by both
SystemCheckPage (wizard) and the new DiagnosePage (settings) instead
of duplicating it.
Documents the Checks/ + SystemCheckPage feature (gating rule, check list,
FreshInstall-only placement) and the ExecutableResolver/.cmd-shim wiring in
ClaudeDo.Worker's ClaudeCliPreflight/ClaudeProcess. Adds docs/explore-notes/installer-preflight.md
(bumped/corrected against the actual implementation) and links it from the
explore-notes README and root CLAUDE.md.
The underlying code lives only on unmerged task branches (06aca9b3.../40272c0b...),
not on main yet, and two follow-up features ("Claude Help Me" button, Config-mode
Diagnose section) were never implemented because they blocked on that same missing
merge. Both gaps are called out explicitly in the new docs and in docs/open.md's
verification checklist, instead of being asserted as done.
MaxTimeoutSeconds was 170s against runs that take tens of minutes, forcing
a dozen full-context wait rounds per long-running batch. Raise it to 900s
and raise MCP_TOOL_TIMEOUT in lockstep (ClaudeProcess + every
InteractiveLaunchSpecService launch spec) to 930000ms so the client
connection actually stays open that long instead of aborting first.
Add get_queue_state (QueueStateMcpTools): configured vs. effective
parallel-slot count (via QueueService.GetSlotCountsAsync, extracted from
the former GetEffectiveMaxParallelAsync), active slots with taskId +
startedAt including the run_task_now override slot, and queued tasks in
pick order -- so a caller can observe queue occupancy instead of inferring
it from maxParallelExecutions.