Files
ClaudeDo/tests/ClaudeDo.Worker.Tests/Infrastructure/TaskStateServiceBuilder.cs
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

47 lines
1.4 KiB
C#

using ClaudeDo.Data;
using ClaudeDo.Worker.Hub;
using ClaudeDo.Worker.Planning;
using ClaudeDo.Worker.Queue;
using ClaudeDo.Worker.State;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
namespace ClaudeDo.Worker.Tests.Infrastructure;
/// Test-only helper that wires TaskStateService and PlanningChainCoordinator
/// against a shared DB factory, breaking the Func cycle between them.
public static class TaskStateServiceBuilder
{
public sealed record Built(
TaskStateService State,
PlanningChainCoordinator Chain,
CapturingHubContext Hub,
Func<int> WakeCount,
CountingQueueWaker Waker);
public static Built Build(IDbContextFactory<ClaudeDoDbContext> dbFactory)
{
var hub = new CapturingHubContext();
var broadcaster = new HubBroadcaster(hub);
var waker = new CountingQueueWaker();
TaskStateService? state = null;
var chain = new PlanningChainCoordinator(dbFactory, () => state!);
state = new TaskStateService(
dbFactory,
broadcaster,
waker,
chain,
NullLogger<TaskStateService>.Instance);
return new Built(state, chain, hub, () => waker.Count, waker);
}
}
public sealed class CountingQueueWaker : IQueueWaker
{
private int _count;
public int Count => Volatile.Read(ref _count);
public void Wake() => Interlocked.Increment(ref _count);
}