StreamingClaudeSession raises onQueueChanged (pending snapshot) and onUserMessageSent
(on delivery, incl. the seeded first prompt); InteractiveSessionService forwards these
as InteractiveQueueChanged/InteractiveMessageSent broadcasts. Lets the UI show queued
messages above the input and move a message into the transcript only when actually
delivered to Claude. Client events + fakes updated.
StreamingClaudeSession now buffers a mid-turn user message in a FIFO queue and
flushes one when the turn's result arrives (no implicit interrupt). InterruptAsync
only writes the control_request (no-op when idle); the resulting turn-end then
flushes any queued message. New InteractiveSessionService.InterruptAsync +
WorkerHub.InterruptInteractiveSession + IWorkerClient.InterruptInteractiveSessionAsync.
StreamingClaudeSession drives claude --input-format stream-json over a kept-
open stdin: sends user messages, interrupts the in-flight turn via the verified
control_request protocol, and tracks turn state from result events (treating an
interrupt-aborted error_during_execution result as turn-ended). IClaudeStreamTransport
abstracts the process I/O so it is unit-tested with a fake (no real claude).
LiveSessionRegistry maps taskId -> live session for the hub to route into.
Backs the upcoming in-app interactive sessions; autonomous task execution untouched.
A running task can call mcp__claudedo_run__AskUser(question) to block (up to 3
min) on a human answer. PendingQuestionRegistry holds the pending question +
TaskCompletionSource; the tool broadcasts TaskQuestionAsked, awaits the answer
(WorkerHub.AnswerTaskQuestion resolves it), and returns it as the tool result —
or a 'proceed on your judgment' fallback on timeout. The run stays Running
throughout (no status/schema change). ClaudeProcess raises MCP_TOOL_TIMEOUT so
the 60s HTTP-MCP cap doesn't kill the wait; the run MCP is now wired for every
task, not just standalone ones. System prompt updated to reconcile 'unattended'.
TaskRunner appends attached files (absolute paths) to the run prompt as the
read-only Reference files section. Task and list deletes now remove the
on-disk attachment dir eagerly, and a startup AttachmentOrphanRecovery sweep
drops any attachments/<taskId>/ whose task no longer exists (covers list
cascade and planning-discard paths).
The picker claims Queued->Running atomically before dispatch; the new
StartRunningAsync guard then rejected every queue-dispatched run. Add
alreadyClaimed to RunAsync/ContinueAsync (queue passes true, override
slot keeps the guard) and align the routing tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Befund (bestätigt): src/ClaudeDo.Worker/Runner/TaskRunner.cs:101 (RunAsync) und :211 (ContinueAsync) ignorieren das TransitionResult von _state.StartRunningAsync. Race-Szenario: Der QueuePicker claimt Queued→Running atomar; ruft der Override-Pfad (RunNow) kurz danach RunAsync für denselben Task auf, schlägt StartRunningAsync fehl (0 rows affected), der Runner startet Claude aber trotzdem → derselb
ClaudeDo-Task: 44f86be2-7f3d-462e-98b3-eb94c0174eea
- Extract FakeClaudeProcess to Infrastructure/FakeClaudeProcess.cs (was
defined inline in QueueServiceTests #region); all consumers updated
- Replace duplicate FakeHubContext/FakeHubClients/FakeClientProxy
(QueueServiceTests) with existing CapturingHubContext from Infrastructure
across all 7 affected files; Planning's file-local FakeHubContext kept
- Rename SeedListWithAgentTag → SeedListAsync (return Task<string>, drop
unused agentTagId tuple element) and SeedListWithAgentTagAsync → SeedListAsync
- PrimeRunnerTests keeps its private nested FakeClaudeProcess: constructor
API (delay/exitCode/lines/result params) differs from the shared one and
replacement would require rewriting every test in that file
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Collapse TryCompleteParentAsync (planning -> Done) and
TryAdvanceImprovementParentAsync (improvement -> WaitingForReview) into one
TryAdvanceParentAsync that surfaces any WaitingForChildren parent for review
once all children are terminal. Planning parents no longer auto-complete.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1. ArgumentList (fix injection): ClaudeArgsBuilder.Build() now returns
IReadOnlyList<string>; ClaudeProcess populates ProcessStartInfo.ArgumentList
instead of Arguments, so values like system prompts are never shell-split.
DailyPrepPrompt, RefinePrompt, and WeekReportService migrated similarly.
All IClaudeProcess fakes updated.
2. ContinueAsync exception guard: wrap RunOnceAsync in try/catch matching
the RunAsync pattern so an unexpected exception never leaves the task
stuck in Running status.
3. Planning chain cascade: OnChildFinishedAsync now calls CancelAsync on
the immediate blocked successor when a child fails or is cancelled,
triggering a recursive cascade that clears the entire remaining chain
instead of leaving it wedged.
4. FailAsync guard: restrict valid source states to Running and Queued;
WaitingForReview -> Failed is now rejected, preventing an invalid
transition that could corrupt the review workflow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A test that spawns the actual claude binary shouldn't live in the suite —
dotnet test must never invoke Claude. §1.0 step 3 stays a manual check.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Spawns the actual claude binary and asserts exit code 0, a session id,
non-empty result, and output tokens > 0 (plan-verification §1.0 step 3).
Inert unless CLAUDE_AUTHENTICATED=1, since it needs an authenticated CLI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
If WorktreeAddAsync succeeds but the worktrees-row insert throws, the
worktree was left on disk and branch undeleted with nothing tracking it.
Wrap the insert in try/catch and best-effort remove the worktree+branch
(non-cancellable) before rethrowing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
BUNDLE — both fixes live in the Worker run-recording / persistence layer (where a TaskRun is written after an agent finishes), NOT in ExternalMcpService.cs. Keep this disjoint from the MCP-surface bundle so the two can run in parallel without worktree conflicts. The DTO fields (tokensIn, tokensOut, resultMarkdown) already exist and are surfaced by list_runs/get_run — the bug is at write time.
1.
ClaudeDo-Task: 49a6060a-5044-4f1b-8665-5cfc064b8a82
Move interface declarations into per-area Interfaces/ subfolders, merge the
small task-list filter classes into StatusFilter/SmartFlagFilter, and simplify
related services, converters and hub DTO handling.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Slice 6 of the worker state and queue consolidation refactor.
* Drop Manual, Planning, Planned, Draft, Waiting from the TaskStatus enum
and from the EF value converter; only the lifecycle values remain
(Idle, Queued, Running, Done, Failed, Cancelled).
* Add migration RetireLegacyTaskStatus that rewrites existing rows:
manual/draft -> idle, planning -> idle+planning_phase=active,
planned -> idle+planning_phase=finalized, waiting -> queued+blocked_by
derived from sort_order via a CTE with LAG().
* Reroute every call site that compared/set legacy values to the new
three-field model (Status + PlanningPhase + BlockedByTaskId), including
the planning repo helpers, MCP services, the planning chain coordinator,
and the UI view-models. TaskRowViewModel now exposes PlanningPhase to
drive the planning badge.
* Refresh Worker/CLAUDE.md and Data/CLAUDE.md, the docs/plan.md status
section, and the planning verification notes in docs/open.md.
Map legacy "bypassPermissions" config to "auto" at dispatch time; pass-through other modes (acceptEdits, plan, default).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add GitService.CheckoutBranchAsync; compare targetBranch to current HEAD
before MergeNoFfAsync and switch when they differ. Returns Blocked if the
branch does not exist. Add three new tests (two service, one GitService).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Worker and App Program.cs: replace SqliteConnectionFactory+SchemaInitializer
with AddDbContextFactory<ClaudeDoDbContext> + Database.Migrate(). Repos
changed from AddSingleton to AddScoped.
All singleton services (QueueService, StaleTaskRecovery, WorktreeManager,
TaskRunner) and singleton ViewModels (MainWindowViewModel, TaskDetailViewModel,
TaskListViewModel, TaskEditorViewModel) now take IDbContextFactory<ClaudeDoDbContext>
and create short-lived contexts per operation.
Test infrastructure: DbFixture now uses EF migrations instead of SchemaInitializer;
all test classes create contexts via DbFixture.CreateContext().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- claude process: run stdout/stderr reads without ct; rely on
kill-on-cancel closing the pipes to unblock them — previously
ReadLineAsync(ct) could hang, stalling task slots and shutdown
- task runner: terminal db writes (task_runs, MarkDone, MarkFailed,
SetLogPath) now use CancellationToken.None; RunOnceAsync catches
OCE and finalizes the run row so ContinueAsync can resume
- task repository: GetNextQueuedAgentTaskAsync is now a single
UPDATE ... RETURNING statement — closes TOCTOU window where two
loop iterations could dispatch the same queued task
- queue service: dispose CancellationTokenSource in slot-completion
ContinueWith to stop leaking wait handles
- git service: register ct.Kill(processTree), drain reads without ct,
always reap via WaitForExitAsync(None) — no more git zombies on
cancelled worktree ops
- worktree manager: branch name uses full task id (dashes stripped)
instead of 8-char prefix, eliminating collision risk
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GitService (in ClaudeDo.Data so the UI can reuse it) wraps the git CLI:
IsGitRepo, RevParseHead, WorktreeAdd/Remove, HasChanges, AddAll, Commit
(multi-line via -F -), DiffStat, BranchDelete, MergeFfOnly. Throws with
stderr on failure.
WorktreeManager owns the per-task lifecycle: validate working_dir is a
git repo (throws if not, no DB row written), create the worktree at
<repo>/../.claudedo-worktrees/<slug>/<id>/ (or central root per config),
insert the worktrees row. CommitIfChangedAsync skips when there are no
changes, otherwise commits and updates head_commit + diff_stat.
CommitMessageBuilder produces "{type}({list-slug}): {title<=60}" with a
blank-line-separated description (truncated to 400) and a permanent
"ClaudeDo-Task: <id>" trailer. Slug normalises whitespace + strips
non-alphanumerics. Newlines hard-coded to \n so git on Windows doesn't
choke on \r\n.
TaskRunner branches on list.WorkingDir: worktree path runs Claude in the
worktree, commits on success, broadcasts WorktreeUpdated; failure leaves
the worktree row active for inspection. Sandbox path unchanged.
Tests: 38 pass (12 new). GitRepoFixture spins up a real temp repo with a
seed commit; tests skip gracefully if `git` isn't on PATH.
CommitMessageBuilder fully unit-tested. WorktreeManager covers create,
no-change skip, real-commit, and non-repo failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Runner stack (non-worktree path): IClaudeProcess + ClaudeProcess spawning the
CLI with --output-format stream-json, prompt via stdin, parses the final
type:"result" line into RunResult. LogWriter appends ndjson to
~/.todo-app/logs/<taskId>.ndjson. TaskRunner orchestrates DB transitions
(MarkRunning -> MarkDone/Failed) and pushes TaskStarted/Message/Finished/
Updated via HubBroadcaster. Worktree-backed lists short-circuit with a
"Slice E" failure message until git support lands.
QueueService (BackgroundService) holds two in-memory slots (_queueSlot +
_overrideSlot) guarded by a lock. Uses PeriodicTimer + SemaphoreSlim wake
signal so WakeQueue() triggers an instant pickup. RunNow throws
InvalidOperationException when override busy; CancelTask cancels the linked
CTS which kills the child process tree.
WorkerHub extended with GetActive, RunNow (translated to HubException
variants), CancelTask, WakeQueue. HubBroadcaster exposes typed push methods.
Tests: 26 pass (12 new). QueueServiceTests cover override-busy,
schedule-filter, FIFO sequentiality, cancellation, plus a FakeClaudeProcess
that blocks on a TCS for deterministic slot-state assertions.
MessageParserTests cover result extraction + malformed/non-result lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>