feat(daily-prep): add RunDailyPrepNow hub method and expose DailyPrepMaxTasks

This commit is contained in:
mika kuns
2026-06-03 16:30:23 +02:00
parent 20b3a29d08
commit 2d00160283
8 changed files with 32 additions and 6 deletions

View File

@@ -52,6 +52,7 @@ public interface IWorkerClient : INotifyPropertyChanged
Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default); Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default);
Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end); Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end);
Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end); Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end);
Task<bool> RunDailyPrepNowAsync();
Task<AppSettingsDto?> GetAppSettingsAsync(); Task<AppSettingsDto?> GetAppSettingsAsync();
Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day); Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day);
Task<DailyNoteDto?> AddDailyNoteAsync(DateOnly day, string text); Task<DailyNoteDto?> AddDailyNoteAsync(DateOnly day, string text);

View File

@@ -334,6 +334,9 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
public Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end) public Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end)
=> _hub.InvokeAsync<string>("GenerateWeekReport", IsoDay(start), IsoDay(end)); => _hub.InvokeAsync<string>("GenerateWeekReport", IsoDay(start), IsoDay(end));
public Task<bool> RunDailyPrepNowAsync()
=> _hub.InvokeAsync<bool>("RunDailyPrepNow");
public async Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day) public async Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day)
=> await TryInvokeAsync<List<DailyNoteDto>>("GetDailyNotes", IsoDay(day)) ?? new List<DailyNoteDto>(); => await TryInvokeAsync<List<DailyNoteDto>>("GetDailyNotes", IsoDay(day)) ?? new List<DailyNoteDto>();
@@ -496,7 +499,8 @@ public sealed record AppSettingsDto(
bool WorktreeAutoCleanupEnabled, bool WorktreeAutoCleanupEnabled,
int WorktreeAutoCleanupDays, int WorktreeAutoCleanupDays,
string? ReportExcludedPaths, string? ReportExcludedPaths,
int StandupWeekday); int StandupWeekday,
int DailyPrepMaxTasks);
public sealed record WorktreeCleanupDto(int Removed); public sealed record WorktreeCleanupDto(int Removed);
public sealed record WorktreeResetDto(int Removed, int TasksAffected, bool Blocked, int RunningTasks); public sealed record WorktreeResetDto(int Removed, int TasksAffected, bool Blocked, int RunningTasks);

View File

@@ -22,6 +22,8 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
[ObservableProperty] private bool _isBusy; [ObservableProperty] private bool _isBusy;
[ObservableProperty] private string _statusMessage = ""; [ObservableProperty] private string _statusMessage = "";
private int _loadedDailyPrepMaxTasks = 5;
public Action? CloseAction { get; set; } public Action? CloseAction { get; set; }
public SettingsModalViewModel(WorkerClient worker, PrimeClaudeTabViewModel prime, public SettingsModalViewModel(WorkerClient worker, PrimeClaudeTabViewModel prime,
@@ -60,6 +62,7 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
: string.Join(Environment.NewLine, : string.Join(Environment.NewLine,
System.Text.Json.JsonSerializer.Deserialize<List<string>>(dto.ReportExcludedPaths) ?? new()); System.Text.Json.JsonSerializer.Deserialize<List<string>>(dto.ReportExcludedPaths) ?? new());
General.StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday; General.StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday;
_loadedDailyPrepMaxTasks = dto.DailyPrepMaxTasks < 1 ? 5 : dto.DailyPrepMaxTasks;
} }
else StatusMessage = Loc.T("vm.settingsModal.workerOffline"); else StatusMessage = Loc.T("vm.settingsModal.workerOffline");
@@ -91,7 +94,8 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
System.Text.Json.JsonSerializer.Serialize( System.Text.Json.JsonSerializer.Serialize(
General.ReportExcludedPaths General.ReportExcludedPaths
.Split('\n').Select(l => l.Trim().TrimEnd('\r')).Where(l => l.Length > 0).ToList()), .Split('\n').Select(l => l.Trim().TrimEnd('\r')).Where(l => l.Length > 0).ToList()),
General.StandupWeekday); General.StandupWeekday,
_loadedDailyPrepMaxTasks);
await _worker.UpdateAppSettingsAsync(dto); await _worker.UpdateAppSettingsAsync(dto);
await Prime.SaveAsync(); await Prime.SaveAsync();
CloseAction?.Invoke(); CloseAction?.Invoke();

View File

@@ -31,7 +31,8 @@ public record AppSettingsDto(
bool WorktreeAutoCleanupEnabled, bool WorktreeAutoCleanupEnabled,
int WorktreeAutoCleanupDays, int WorktreeAutoCleanupDays,
string? ReportExcludedPaths, string? ReportExcludedPaths,
int StandupWeekday); int StandupWeekday,
int DailyPrepMaxTasks);
public record WorktreeCleanupDto(int Removed); public record WorktreeCleanupDto(int Removed);
public record WorktreeResetDto(int Removed, int TasksAffected, bool Blocked, int RunningTasks); public record WorktreeResetDto(int Removed, int TasksAffected, bool Blocked, int RunningTasks);
@@ -79,6 +80,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
private readonly PlanningMergeOrchestrator _planningMergeOrchestrator; private readonly PlanningMergeOrchestrator _planningMergeOrchestrator;
private readonly PlanningChainCoordinator _planningChain; private readonly PlanningChainCoordinator _planningChain;
private readonly IPrimeScheduleSignal _primeSignal; private readonly IPrimeScheduleSignal _primeSignal;
private readonly IPrimeRunner _primeRunner;
private readonly ITaskStateService _state; private readonly ITaskStateService _state;
private readonly IWeekReportService _report; private readonly IWeekReportService _report;
@@ -98,6 +100,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
PlanningMergeOrchestrator planningMergeOrchestrator, PlanningMergeOrchestrator planningMergeOrchestrator,
PlanningChainCoordinator planningChain, PlanningChainCoordinator planningChain,
IPrimeScheduleSignal primeSignal, IPrimeScheduleSignal primeSignal,
IPrimeRunner primeRunner,
ITaskStateService state, ITaskStateService state,
IWeekReportService report) IWeekReportService report)
{ {
@@ -116,6 +119,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
_planningMergeOrchestrator = planningMergeOrchestrator; _planningMergeOrchestrator = planningMergeOrchestrator;
_planningChain = planningChain; _planningChain = planningChain;
_primeSignal = primeSignal; _primeSignal = primeSignal;
_primeRunner = primeRunner;
_state = state; _state = state;
_report = report; _report = report;
} }
@@ -217,7 +221,8 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
row.WorktreeAutoCleanupEnabled, row.WorktreeAutoCleanupEnabled,
row.WorktreeAutoCleanupDays, row.WorktreeAutoCleanupDays,
row.ReportExcludedPaths, row.ReportExcludedPaths,
row.StandupWeekday); row.StandupWeekday,
row.DailyPrepMaxTasks);
} }
public async Task UpdateAppSettings(AppSettingsDto dto) public async Task UpdateAppSettings(AppSettingsDto dto)
@@ -238,6 +243,7 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
WorktreeAutoCleanupDays = dto.WorktreeAutoCleanupDays, WorktreeAutoCleanupDays = dto.WorktreeAutoCleanupDays,
ReportExcludedPaths = dto.ReportExcludedPaths, ReportExcludedPaths = dto.ReportExcludedPaths,
StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday, StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday,
DailyPrepMaxTasks = dto.DailyPrepMaxTasks,
}); });
} }
@@ -533,6 +539,15 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
_primeSignal.Signal(); _primeSignal.Signal();
} }
public async Task<bool> RunDailyPrepNow()
{
var schedule = new PrimeScheduleDto(Guid.Empty, 0, TimeSpan.Zero, true, null, null);
var firedAt = DateTimeOffset.Now;
var outcome = await _primeRunner.FireAsync(schedule, Context.ConnectionAborted);
await _broadcaster.PrimeFired(Guid.Empty, outcome.Success, outcome.Message, firedAt);
return outcome.Success;
}
private static DateOnly Day(string iso) => DateOnly.ParseExact(iso, "yyyy-MM-dd", CultureInfo.InvariantCulture); private static DateOnly Day(string iso) => DateOnly.ParseExact(iso, "yyyy-MM-dd", CultureInfo.InvariantCulture);
public Task<string?> GetWeekReport(string startIso, string endIso) => public Task<string?> GetWeekReport(string startIso, string endIso) =>

View File

@@ -62,6 +62,7 @@ public abstract class StubWorkerClient : IWorkerClient
public virtual Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask; public virtual Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask;
public virtual Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult<string?>(null); public virtual Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult<string?>(null);
public virtual Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult(""); public virtual Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult("");
public virtual Task<bool> RunDailyPrepNowAsync() => Task.FromResult(false);
public virtual Task<AppSettingsDto?> GetAppSettingsAsync() => Task.FromResult<AppSettingsDto?>(null); public virtual Task<AppSettingsDto?> GetAppSettingsAsync() => Task.FromResult<AppSettingsDto?>(null);
public virtual Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day) => Task.FromResult(new List<DailyNoteDto>()); public virtual Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day) => Task.FromResult(new List<DailyNoteDto>());
public virtual Task<DailyNoteDto?> AddDailyNoteAsync(DateOnly day, string text) => Task.FromResult<DailyNoteDto?>(null); public virtual Task<DailyNoteDto?> AddDailyNoteAsync(DateOnly day, string text) => Task.FromResult<DailyNoteDto?>(null);

View File

@@ -55,7 +55,7 @@ public sealed class PlanningHubTests : IDisposable
{ {
var hub = new WorkerHub( var hub = new WorkerHub(
null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!,
_planning, _launcher, null!, null!, null!, null!, null!, null!); _planning, _launcher, null!, null!, null!, null!, null!, null!, null!);
hub.Clients = new FakeHubCallerClients(_proxy); hub.Clients = new FakeHubCallerClients(_proxy);
hub.Context = new FakeHubCallerContext(); hub.Context = new FakeHubCallerContext();
return hub; return hub;

View File

@@ -19,7 +19,7 @@ public sealed class WorktreeStateHubTests : IDisposable
var broadcaster = new HubBroadcaster(new CapturingHubContext()); var broadcaster = new HubBroadcaster(new CapturingHubContext());
var hub = new WorkerHub( var hub = new WorkerHub(
null!, null!, null!, null!, broadcaster, _db.CreateFactory(), null!, null!, null!, null!, broadcaster, _db.CreateFactory(),
null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!); null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!);
hub.Clients = new FakeHubCallerClients(new RecordingClientProxy()); hub.Clients = new FakeHubCallerClients(new RecordingClientProxy());
hub.Context = new FakeHubCallerContext(); hub.Context = new FakeHubCallerContext();
return hub; return hub;

View File

@@ -76,6 +76,7 @@ sealed class FakeWorkerClient : IWorkerClient
public Task<AppSettingsDto?> GetAppSettingsAsync() => Task.FromResult<AppSettingsDto?>(null); public Task<AppSettingsDto?> GetAppSettingsAsync() => Task.FromResult<AppSettingsDto?>(null);
public Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult<string?>(null); public Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult<string?>(null);
public Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult(""); public Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult("");
public Task<bool> RunDailyPrepNowAsync() => Task.FromResult(false);
public Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day) => Task.FromResult(new List<DailyNoteDto>()); public Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day) => Task.FromResult(new List<DailyNoteDto>());
public Task<DailyNoteDto?> AddDailyNoteAsync(DateOnly day, string text) => Task.FromResult<DailyNoteDto?>(null); public Task<DailyNoteDto?> AddDailyNoteAsync(DateOnly day, string text) => Task.FromResult<DailyNoteDto?>(null);
public Task UpdateDailyNoteAsync(string id, string text) => Task.CompletedTask; public Task UpdateDailyNoteAsync(string id, string text) => Task.CompletedTask;