Commit Graph

135 Commits

Author SHA1 Message Date
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
8823265e5a refactor(worker/state): introduce TaskStateService and route mutations through it
Slice 2 of the worker state consolidation refactor (spec sections 2 and 8).

Adds Worker/State/ITaskStateService + TaskStateService as the single component
that mutates Status, PlanningPhase, and BlockedByTaskId. Each transition is one
atomic ExecuteUpdate with a WHERE filter on the expected source status, so
parallel claims are TOCTOU-free. Side effects (queue wake on -> Queued, hub
TaskUpdated broadcast, chain advance + parent completion on terminal child)
are owned by the service so callers no longer need to remember them.

Migrated callers (mechanical, behavior preserved):
- TaskRunner: HandleSuccess/HandleFailure/MarkFailed/RunAsync/ContinueAsync
- StaleTaskRecovery: bulk recover stale Running tasks
- TaskResetService: status flip (worktree cleanup stays in service)
- PlanningSessionManager.StartAsync: status flip via state, token write via repo
- PlanningChainCoordinator.OnChildFinishedAsync: routes the next-sibling write
  through state.UnblockAsync (Slice 4 finishes the rewrite)
- ExternalMcpService.UpdateTaskStatus: Queued case via state.EnqueueAsync

Repo Mark*Async helpers (MarkRunning/MarkDone/MarkFailed/FlipAllRunningToFailed)
are now internal; ClaudeDo.Data grants InternalsVisibleTo to ClaudeDo.Worker
and ClaudeDo.Worker.Tests for the existing repo-level tests.

DI: TaskStateService is registered as Singleton in both the main app and the
external-MCP app; the queue-wake delegate captures sp -> QueueService.WakeQueue
to break the TaskStateService -> QueueService -> TaskRunner -> TaskStateService
construction cycle. PlanningChainCoordinator takes Func<ITaskStateService> for
the same reason; Slice 3 will replace both with IQueueWaker.

Tests: TaskStateServiceTests covers happy + reject for every transition, the
parallel StartRunningAsync claim race, child-terminal chain advancement, and
stale recovery. Existing service/repo tests are updated to construct the new
state-service via a TaskStateServiceBuilder helper. Pre-existing constructor
drift in QueueService/ExternalMcp/PlanningHub tests is patched to keep the
test project building (the surrounding test logic is otherwise untouched).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 11:31:57 +02:00
Mika Kuns
2d7f825ff3 feat(mcp/planning): allow status changes and post-finalize edits in active session
Extend UpdateChildTask with a status parameter (restricted to Draft, Manual,
Queued, Waiting) and replace the 'only Draft is editable' rule with 'planning
session is active'. Same loosening applied to DeleteChildTask. Lets planning
agents iterate on children that already escaped Draft state.
2026-04-27 10:16:32 +02:00
mika kuns
59dc1e2357 feat(mcp/external): add SetTaskTags 2026-04-25 11:29:58 +02:00
mika kuns
31a394e694 feat(mcp/external): add DeleteTask
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 11:28:47 +02:00
mika kuns
d99cb68afb feat(mcp/external): add UpdateTask for content/tag patching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 11:27:16 +02:00
mika kuns
1a74e1c058 feat(mcp/external): AddTask accepts tags on creation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 11:25:42 +02:00
mika kuns
e6846b7e6d feat(mcp/external): add ListTags + inject TagRepository
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 11:24:10 +02:00
mika kuns
e767d57640 test(external): scaffold ExternalMcpServiceTests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 11:21:13 +02:00
mika kuns
25493528de feat(data): add TaskRepository.SetTagsAsync for full tag-set replacement 2026-04-25 11:18:26 +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
2ad6f20258 chore: bump default max turns from 30 to 100
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 09:35:01 +02:00
mika kuns
b2eb5fcfa4 refactor(worker): use --permission-mode auto instead of --dangerously-skip-permissions
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>
2026-04-25 09:34:48 +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
e58cac24e1 feat(worker): add pre-flight checks and idempotent restart to PlanningMergeOrchestrator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:24:41 +02:00
mika kuns
b9896399fa feat(worker): add PlanningMergeOrchestrator.AbortAsync 2026-04-24 18:18:49 +02:00
mika kuns
7d87c03cfa feat(worker): add PlanningMergeOrchestrator.ContinueAsync to resume merge after conflict
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:15:19 +02:00
mika kuns
3142ba203f feat(worker): add PlanningMergeOrchestrator happy path with merge event broadcasts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:08:58 +02:00
mika kuns
bc788e1e0f feat(ui): add conflict resolution dialog for planning merge-all
Opens a modal when PlanningMergeConflict fires, listing conflicted files
with options to open in VS Code, continue, or abort the merge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:08:45 +02:00
mika kuns
a6ebff3f34 feat(ui): add aggregated diff viewer for planning tasks
Implements Task 14: PlanningDiffView (Window), PlanningDiffViewModel,
ShowPlanningDiffModal callback wired in DetailsIslandView, and 5 xUnit tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:39:38 +02:00
mika kuns
389d9045d5 feat(worker): add PlanningAggregator.CleanupIntegrationBranchAsync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:34:25 +02:00
mika kuns
1aead9dad0 refactor(ui): test planning detail pane via real ViewModel and restore merge-all IsEnabled binding
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:31:54 +02:00
mika kuns
9d04d1d9f6 fix(worker): reorder PlanningAggregator checkout/delete and kill git on cancel
Also stub new IWorkerClient planning members in FakeWorkerClient to restore build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:24:24 +02:00
mika kuns
4c6fd9f024 feat(ui): add merge-target dropdown and merge-all controls to planning detail
- Add SubtaskDiffDto and CombinedDiffResultDto to PlanningDtos.cs
- Extend IWorkerClient with 5 planning merge methods and 5 events
- Implement methods and hub subscriptions on WorkerClient
- Add Status and WorktreeState to SubtaskRowViewModel
- Add MergeTargetBranches, SelectedMergeTarget, CanMergeAll,
  MergeAllDisabledReason, MergeAllError, RecomputeCanMergeAll,
  MergeAllCommand, ReviewCombinedDiffCommand (Task 14 TODO)
  to DetailsIslandViewModel
- Add planning merge section to DetailsIslandView.axaml
  (merge target ComboBox + buttons + error label), gated on
  Task.IsPlanningParent
- Add 4 xUnit tests covering CanMergeAll logic and DTO shape

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:22:27 +02:00
mika kuns
2cab33d708 feat(worker): add PlanningAggregator.BuildIntegrationBranchAsync 2026-04-24 16:18:45 +02:00
mika kuns
a1727b647c feat(worker): add PlanningAggregator.GetAggregatedDiffAsync
Returns per-subtask diff entries (title, branch, base/head commit, DiffStat, unified diff) for all children of a Planning task in SortOrder order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:08:56 +02:00
mika kuns
6bdfa73150 fix(ui): align virtual list semantics and complete planning roll-up coverage 2026-04-24 16:03:27 +02:00
mika kuns
6d460ea996 fix(ui): planning parents roll up child status; children stay nested until parent Done 2026-04-24 15:47:47 +02:00
mika kuns
bc0f1e3122 feat(worker): add AbortMergeAsync to cancel a conflicted merge 2026-04-24 15:42:15 +02:00
mika kuns
62106ff644 feat(worker): add ContinueMergeAsync to resume a conflicted merge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:17:57 +02:00
mika kuns
e77ba35b0e feat(worker): add leaveConflictsInTree option to TaskMergeService.MergeAsync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:06:33 +02:00
mika kuns
5a03dc8430 feat(worker): broadcast child TaskUpdated events on planning CRUD
So the UI refreshes individual child rows alongside the parent during
create/update/delete/finalize from the planning MCP service.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:54:46 +02:00
mika kuns
a5ebfd12f8 test(worker): clean up non-git test tmp dir and assert self-heal setup exit code
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:24:01 +02:00
mika kuns
2262ab0e13 test(worker): cover planning worktree lifecycle and self-heal
Adds four tests to PlanningSessionManagerTests: worktree removal on
discard, error on non-git working dir, self-heal when branch already
exists, and resume returning the correct token and session id.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:20:29 +02:00
mika kuns
0da527dbbc test(worker): adapt planning tests to git-backed worktree flow
Update constructor calls (6-arg), seed AppSettings with sibling strategy,
git-init working dirs via GitRepoFixture.InitRepoWithInitialCommit, and
replace McpConfigPath assertions with worktree-path / .mcp.json checks.
Also fixes PlanningHubTests which had the same 3-arg constructor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:14:46 +02:00
mika kuns
b7c60f5838 feat(ui): live task updates from worker events + planning polish
Wire TasksIslandViewModel to TaskUpdated/WorktreeUpdated/TaskMessage worker
events so rows refresh without a full reload; add ForegroundHelper to permit
wt.exe to take foreground on planning launch; misc UI polish on lists, task
rows and settings modal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:12:27 +02:00
mika kuns
0782ba574b test(worker): planning session end-to-end 2026-04-23 23:31:01 +02:00
mika kuns
7b67e35720 feat(worker): SignalR hub endpoints for planning sessions 2026-04-23 23:26:12 +02:00
mika kuns
6cb20a9213 feat(worker): map MCP HTTP endpoint and broadcast TaskUpdated
- Add PlanningMcpContextAccessor (Option A) to read PlanningMcpContext
  from HttpContext.Items set by PlanningTokenAuthMiddleware
- Annotate PlanningMcpService with [McpServerToolType]/[McpServerTool]
  and remove PlanningMcpContext ctx parameter from all tool methods
- Broadcast TaskUpdated(parentTaskId) via HubBroadcaster after every
  mutation in PlanningMcpService
- Refactor PlanningSessionManager to accept IDbContextFactory for
  singleton-safe use in DI; keep direct-repo ctor for tests
- Register PlanningSessionManager (singleton), IPlanningTerminalLauncher,
  PlanningMcpContextAccessor, PlanningMcpService, and MCP server in
  Program.cs; wire PlanningTokenAuthMiddleware and MapMcp("/mcp")
- Update PlanningMcpServiceTests with fake HttpContext accessor and
  no-op HubBroadcaster (avoids Moq dependency)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 23:12:24 +02:00
mika kuns
99c6a71e4c feat(worker): MCP tools update_planning_task and finalize 2026-04-23 23:03:42 +02:00
mika kuns
0088d6e0e0 feat(worker): MCP tools for child-task CRUD 2026-04-23 22:57:27 +02:00
mika kuns
43a3740980 feat(worker): WindowsTerminalPlanningLauncher with pre-flight checks 2026-04-23 21:08:15 +02:00
mika kuns
d28164caf4 feat(worker): PlanningSessionManager.GetPendingDraftCountAsync 2026-04-23 21:04:06 +02:00
mika kuns
77f7cf1423 feat(worker): PlanningSessionManager.FinalizeAsync 2026-04-23 21:01:22 +02:00
mika kuns
84e6c2d5fc feat(worker): PlanningSessionManager.DiscardAsync 2026-04-23 20:58:55 +02:00
mika kuns
84b0ba8670 feat(worker): PlanningSessionManager.ResumeAsync 2026-04-23 20:55:01 +02:00
mika kuns
b6bec1e63c feat(worker): PlanningSessionManager.StartAsync
Add PlanningSessionFiles, PlanningSessionStartContext/ResumeContext DTOs,
PlanningSessionManager.StartAsync (file scaffolding + status transition),
and integration tests. Also fix migration discovery by adding [DbContext]
attribute to all migration classes and switch DbFixture to EnsureCreated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 20:49:41 +02:00