Commit Graph

12 Commits

Author SHA1 Message Date
mika kuns
d4a46420c9 feat(worker): hook TryCompleteParentAsync after MarkDone/MarkFailed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 18:18:50 +02:00
mika kuns
ea4d2d7c0c feat(worker): emit WorkerLog events from TaskRunner 2026-04-23 14:46:10 +02:00
Mika Kuns
4283c67d81 fix(worker): prefix broadcast lines with [stdout] so UI parser routes them 2026-04-21 15:35:40 +02:00
mika kuns
4fb6ba6be8 fix(worker): emit RunCreated after run row exists
Remove premature RunCreated broadcast from WorkerHub.RunNow and the
duplicate calls in RunAsync retry block and ContinueAsync. RunOnceAsync
now owns the broadcast for every run, fired immediately after the row
insert so the UI never receives an event for a non-existent row.
2026-04-17 14:17:00 +02:00
mika kuns
36484ed45a feat(worker,ui): wire EF Core into DI and update all consumers to IDbContextFactory
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>
2026-04-16 08:59:24 +02:00
Mika Kuns
d3b85f2234 fix(worker): address concurrency, cancellation, and resource issues
- 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>
2026-04-15 16:27:18 +02:00
Mika Kuns
8c051d8f62 feat(data): add subtasks table, repository and prompt integration
Per-task checklist backend: subtasks table with CASCADE delete,
SubtaskEntity + SubtaskRepository (connection-per-op, async), DI
registration in App and Worker, TaskRunner composes a '## Sub-Tasks'
markdown block into the Claude prompt when subtasks exist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:19:54 +02:00
Mika Kuns
945a1eef11 feat(worker): default to claude-sonnet-4-6 when no model configured
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 16:20:48 +02:00
Mika Kuns
76473dd92a refactor(worker): rewrite TaskRunner with config resolution, retry, and continue support 2026-04-14 14:02:57 +02:00
Mika Kuns
1cdaaf9fd2 refactor(worker): simplify ClaudeProcess to accept pre-built args and use StreamAnalyzer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 11:45:23 +02:00
Mika Kuns
01235d986f feat(worker,data): add git worktree support and conventional commits
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>
2026-04-13 13:29:26 +02:00
Mika Kuns
e5038d7e16 feat(worker): add claude-cli runner, queue service, and hub api
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>
2026-04-13 12:14:00 +02:00