Commit Graph

75 Commits

Author SHA1 Message Date
mika kuns
ad7c9facaf fix(worker): escape newline/tab in CLI args 2026-04-17 14:25:15 +02:00
mika kuns
11a4376da5 fix(worker): guard against same task in queue and override slot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 14:23:08 +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
3423919655 fix: resolve critical bugs and improve reliability across worker, data, UI
- Fix worker using wrong DB by defaulting to CurrentUser service account
  and expanding ~ to absolute paths at install time
- Fix DbContext disposed before fire-and-forget by passing taskId instead
  of TaskEntity into RunInSlotAsync, which creates its own context
- Fix ActiveTaskDto property casing mismatch between hub and client
- Move WAL mode PRAGMA before migrations to prevent concurrent lock issues
- Replace FirstAsync with FirstOrDefaultAsync + null guards in tag operations
- Add delete confirmation flow for lists
- Log fire-and-forget exceptions instead of swallowing them
- Broadcast RunCreated event from WorkerHub.RunNow
- Add IDisposable to MainWindowViewModel for event handler cleanup
- Preserve subtask CreatedAt on updates instead of overwriting
- Replace bare catch blocks with Debug.WriteLine logging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:12:59 +02:00
mika kuns
611454df1e fix(data): address code review findings
- Fix sort order regression in GetByListIdAsync (was descending, should be ascending)
- Restore WAL mode pragma (was silently dropped in EF migration)
- Add existing-DB compatibility shim in MigrateAndConfigure (baselines InitialCreate
  migration for databases created by the old schema.sql)
- Remove dead AddDbContext/AddScoped registrations from Worker (only IDbContextFactory
  is used by singleton consumers)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 09:10:35 +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
f599f8d0af fix(installer,worker): service hosting, dark theme, uninstall polish
Some checks failed
Release / release (push) Failing after 0s
Worker:
- Wire UseWindowsService + Microsoft.Extensions.Hosting.WindowsServices so
  SCM's Service Control Protocol handshake succeeds. Previously the binary
  exited immediately under sc start, leaving the service registered but
  never running.

Installer:
- Pin SDK to .NET 9 (global.json) — SDK 10 dropped win-arm from its RID
  graph, breaking restore of the WPF project; .NET 9 keeps win-arm AND
  understands the .slnx solution format.
- Force SelfContained=true and default RID=win-x64 when PublishSingleFile
  is set, so Rider Publish and CLI produce the same bundle.
- Dark theme: set Background/Foreground explicitly on WizardWindow and
  SettingsWindow roots (WPF implicit styles don't cascade to derived
  Window types). Custom ComboBox template + ComboBoxItem style so
  dropdowns honour the dark palette instead of system defaults.
- Throttle download progress to one report per MB and overwrite the same
  UI line (\r prefix marker) instead of appending per chunk.
- Register ClaudeDo in HKLM\...\Uninstall so it appears in Apps & Features.
  Copy installer into InstallDir\uninstaller\ for the UninstallString, and
  schedule a cmd.exe trampoline to handle the self-delete case when
  Apps & Features launches the copy from inside the install dir.
- Treat sc.exe stop exit 1062 (ERROR_SERVICE_NOT_ACTIVE) as success.
- Delete the uninstall registry key during UninstallRunner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:19:09 +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
c1c4c75979 refactor(worker): remove MessageParser (replaced by StreamAnalyzer) 2026-04-14 14:12:21 +02:00
Mika Kuns
03728c8e4a docs(worker): update CLAUDE.md with CLI modernization changes 2026-04-14 14:10:46 +02:00
Mika Kuns
adc5a16afc feat(worker): add ContinueTask routing to QueueService 2026-04-14 14:04:28 +02:00
Mika Kuns
6cb8012d82 feat(worker): add ContinueTask, GetAgents, RefreshAgents hub methods and RunCreated broadcast 2026-04-14 14:04:17 +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
8825351526 feat(worker): add AgentFileService for filesystem agent management
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:39:24 +02:00
Mika Kuns
54c4d3cf93 feat(worker): extend RunResult with structured output, session ID, and token metrics
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:38:39 +02:00
Mika Kuns
8b342bcc21 feat(worker): add StreamAnalyzer for rich NDJSON stream parsing 2026-04-14 11:36:58 +02:00
Mika Kuns
dab461cc41 feat(worker): add ClaudeArgsBuilder for dynamic CLI argument construction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:35:46 +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
Mika Kuns
9f51ff0b17 feat(data,worker): add repositories, stale-task recovery, and test foundation
Data: TagRepository, ListRepository, TaskRepository (incl. queue selection via
effective agent tag + scheduled_for filter), WorktreeRepository. All CRUD via
parameterized SqliteCommand; enums roundtrip as lowercase strings matching the
schema CHECK constraints.

Worker: StaleTaskRecovery IHostedService flips running -> failed on startup and
marks the result column with a [stale] reason. All four repositories registered
as singletons.

Tests: DbFixture with temp-file SQLite + schema bootstrap, covering
TaskRepository (queue pick via list-tag and task-tag, schedule filter,
transitions, stale flip), ListRepository CRUD + junctions, and
StaleTaskRecovery. 14 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:08:06 +02:00
Mika Kuns
f81ef02273 feat(data,worker): add db schema init and signalr hub skeleton
Data layer: Paths helper with ~/%USERPROFILE% expansion, SqliteConnectionFactory
(WAL + foreign keys), SchemaInitializer that applies the embedded schema.sql, and
POCO entities for lists/tasks/tags/worktrees.

Worker: WorkerConfig loader (~/.todo-app/worker.config.json with defaults),
WorkerHub exposing Ping(), and Program.cs wiring Kestrel to 127.0.0.1:<port>,
SignalR at /hub, schema applied on startup.

Pins Microsoft.Data.Sqlite and Microsoft.Extensions.Hosting to 8.x for net8.0
compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:00:47 +02:00
Mika Kuns
71cfa64427 chore(scaffold): add solution skeleton for App, Ui, Data, Worker and tests
Create .slnx with five projects, wire references (App->Ui->Data,
Worker->Data, Tests->Worker+Data), install Avalonia/MVVM/SignalR.Client/
Sqlite/Hosting packages, and add schema.sql per docs/plan.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:48:06 +02:00