Commit Graph

23 Commits

Author SHA1 Message Date
mika kuns
5308ba3136 refactor(config): consolidate permission modes into PermissionModeRegistry
Also fixes WorkerHub.UpdateAppSettings falling back to "bypassPermissions"
when AppSettingsEntity and the runtime default are "auto". The fallback
now matches the entity default.
2026-05-19 08:59:16 +02:00
mika kuns
a62ef240d1 refactor(config): consolidate model aliases into ModelRegistry
Replaces three scattered model lists (ListSettingsModalViewModel,
DetailsIslandViewModel, GeneralSettingsTabViewModel) and the hardcoded
planning model with a single source. Planning launcher now uses the
opus alias instead of pinning claude-opus-4-7.
2026-05-19 08:58:43 +02:00
mika kuns
623ebf147b refactor(tags): remove tag entity and all references
Drops TagEntity, TagRepository, and tag wiring across data layer, worker,
and UI. Adds RemoveTags migration to clean up schema.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 08:07:24 +02:00
mika kuns
d094a21e09 feat(planning): prevent orphaned subtasks via guards + startup repair
Three coordinated guards close the orphan-creation paths:

- CreateChildAsync refuses when the parent is not in a planning phase.
- DiscardPlanningAsync now returns a structured DiscardPlanningOutcome
  and refuses when children are queued or running; callers can opt into
  auto-dequeuing queued kids via dequeueQueuedChildren=true. Terminal
  children (Done/Failed/Cancelled) are promoted to top-level instead of
  becoming orphans when the parent's PlanningPhase is reset.
- OrphanRecovery hosted service clears ParentTaskId on any rows whose
  parent is missing or no longer in a planning phase on worker startup,
  mirroring the StaleTaskRecovery pattern.

UI surfaces the block reason: a confirm dialog offers to dequeue queued
children and retry; a running-children block is shown as a hard error
asking the user to cancel first.

WorkerClient now negotiates the JsonStringEnumConverter so the
DiscardPlanningResult enum round-trips correctly over SignalR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 16:02:15 +02:00
mika kuns
121e8cd476 feat(worker): add hub methods to set task status and tags freely
Adds ForceSetStatusAsync on ITaskStateService (no transition guards)
plus SetTaskStatus / SetTaskTags / GetAllTags hub methods so the UI
can edit a task's status and tags directly. PlanningHubTests ctor
updated for the new ITaskStateService dependency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 10:39:44 +02:00
Mika Kuns
507f59f1d1 feat(worker): add Prime schedule hub methods
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 09:04:20 +02:00
Mika Kuns
ff7c239959 refactor(worker): extract OverrideSlotService and reorganize Worker/Services into domain folders
Slice 5 of the worker state consolidation refactor.

OverrideSlotService (new in Worker/Queue/) owns RunNow, ContinueTask,
and the override-slot piece of CancelTask. QueueService keeps the
queue-slot guard for "task is already running" rejection and delegates
to OverrideSlotService for execution; CancelTask tries the override
slot first, then the queue slot. QueueSlotState is extracted to its own
file.

Folder reorg (via git mv to preserve history):
- Worker/Queue/      QueueService, OverrideSlotService, QueueSlotState
                     (alongside existing waker/picker)
- Worker/Lifecycle/  StaleTaskRecovery, TaskResetService, TaskMergeService
- Worker/Worktrees/  WorktreeMaintenanceService
- Worker/Agents/     AgentFileService, DefaultAgentSeeder

Worker/Services/ folder removed. All consumers updated to the new
namespaces (Program.cs, WorkerHub, ExternalMcpService,
PlanningMergeOrchestrator, all Worker tests).

OverrideSlotService is registered as a DI singleton in both the main
worker app and the external MCP app.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:42:13 +02:00
Mika Kuns
4ab906ff0b feat(planning): consolidate finalize+chain via TaskStateService, fix queue pickup
Slice 4 of the worker state consolidation refactor. Eliminates the
"queue never picks up planning tasks" bug structurally by routing both
the manager and MCP finalize paths through TaskStateService and
PlanningChainCoordinator.SetupChainAsync, where the auto-wake on enqueue
guarantees the queue picker claims the first child immediately.

- Delete TaskRepository.FinalizePlanningAsync; PlanningSessionManager
  now orchestrates via _state.FinalizePlanningAsync + _chain.SetupChainAsync.
- Rename QueueSubtasksSequentiallyAsync to SetupChainAsync (internal);
  layout is now Status=Queued + BlockedByTaskId, with auto-attached agent tag.
- OnChildFinishedAsync looks up the successor by BlockedByTaskId, drops
  the legacy Waiting status lookup.
- PlanningMcpService.Finalize routes through state+chain; EditableStatuses
  drops Waiting and adds Idle; gate uses PlanningPhase==Active.
- TaskStateService.FinalizePlanningAsync clears the planning session token.
- UI: TaskRowViewModel adds BlockedByTaskId; IsQueued/IsWaiting reflect
  the new layout; TasksIslandViewModel.RemoveFromQueueAsync clears
  BlockedByTaskId on dequeue.
- New regression test PlanningEndToEndTests.FinalizeAsync_FirstChildIs
  ClaimedByPicker_WithinDeadline asserts the picker claims the first
  child within 200ms with no manual WakeQueue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:16:12 +02:00
Mika Kuns
064a903076 refactor(worker/queue): split queue waker and picker, auto-wake on enqueue
Slice 3 of the worker state and queue consolidation refactor.

- Add IQueueWaker / QueueWaker (singleton holding the wake semaphore).
- Add IQueuePicker / QueuePicker; raw SQL UPDATE...RETURNING moves out of
  TaskRepository.GetNextQueuedAgentTaskAsync (deleted) and now also filters
  on blocked_by_task_id IS NULL and writes started_at on claim.
- TaskStateService takes IQueueWaker directly; the Func<QueueService>
  indirection is gone. State transitions to Queued auto-wake the dispatcher.
- QueueService waits via the shared waker and dispatches via the picker.
- Drop explicit _queue.WakeQueue() calls in WorkerHub.QueuePlanningSubtasksAsync
  and ExternalMcpService.AddTask. The hub WakeQueue endpoint stays for
  diagnostics, delegating to _waker.Wake().
- Migrate tests; pre-existing flaky AppSettings/ExternalMcp tests untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 12:05:54 +02:00
mika kuns
6c54759aa0 feat(ui): add Run interactively action to task context menu
Spawns Windows Terminal in the list working directory running
`claude --permission-mode auto` with the task title and description
prefilled as the initial prompt. Reuses the planning launcher
infrastructure but skips worktree, system prompt, and MCP setup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 10:02:21 +02:00
mika kuns
16e1ddd129 feat(worker): add PlanningChainCoordinator for sequential subtask execution
Coordinates Waiting -> Queued transitions between sibling subtasks: when a child finishes Done, the next Waiting sibling is promoted to Queued. WorkerHub.QueuePlanningSubtasksAsync exposes this to the UI; TaskRunner advances the chain on completion. Also tightens the planning-session prompt: planner must use MCP tools, not direct edits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 09:36:01 +02:00
mika kuns
3008c36921 feat(worker): register planning services and add Merge-all hub methods
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:28:38 +02:00
mika kuns
7b67e35720 feat(worker): SignalR hub endpoints for planning sessions 2026-04-23 23:26:12 +02:00
mika kuns
1a10e6fa09 feat(worker): expose RestoreDefaultAgents hub method 2026-04-23 12:18:49 +02:00
Mika Kuns
7c312161bb feat(worker): add hub methods for list and task agent settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 13:16:46 +02:00
Mika Kuns
c53b5878cf feat(worker): expose MergeTask and GetMergeTargets on WorkerHub 2026-04-22 09:44:22 +02:00
Mika Kuns
219a231f32 feat(worker): expose ResetTask hub method
Wire TaskResetService into DI and add WorkerHub.ResetTask with the
same InvalidOperationException/KeyNotFoundException error-translation
pattern as ContinueTask.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 17:35:37 +02:00
Mika Kuns
e6b37624a1 feat(ui): add settings modal and wire to worker hub 2026-04-21 15:55:53 +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
6cb8012d82 feat(worker): add ContinueTask, GetAgents, RefreshAgents hub methods and RunCreated broadcast 2026-04-14 14:04:17 +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
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