fix(worker): close status-force bypasses on Running tasks
update_task_status Idle/Done, WorkerHub.SetTaskStatus, and the UI's Mark Done/Cancelled menu items could all force a Running task's status without going through TaskStateService's guards, leaving its CLI process untracked and its worktree writes silently dropped. Idle now routes through the already-guarded TaskStateService.ResetToIdleAsync instead of the repo's unconditional ResetToManualAsync; Done and SetTaskStatus explicitly reject Running; the context menu disables both entries with a tooltip while running. TaskRunner also now logs a warning whenever a terminal transition (SubmitForChildren/SubmitForReview/Complete/Fail) is rejected instead of silently discarding the run result. DeleteTask's MCP path gets the same friendly foreign-key message WorkerHub.DeleteTask already had for a task with children.
This commit is contained in:
@@ -586,6 +586,13 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
{
|
||||
if (!Enum.TryParse<TaskStatus>(status, ignoreCase: true, out var parsed))
|
||||
throw new HubException($"unknown status: {status}");
|
||||
|
||||
await using var ctx = await _dbFactory.CreateDbContextAsync(Context.ConnectionAborted);
|
||||
var current = await new TaskRepository(ctx).GetByIdAsync(taskId, Context.ConnectionAborted)
|
||||
?? throw new HubException("task not found");
|
||||
if (current.Status == TaskStatus.Running)
|
||||
throw new HubException("Cannot change the status of a running task. Cancel it first.");
|
||||
|
||||
// Queueing goes through the gated transition so draft subtasks can't be queued;
|
||||
// other statuses keep the unconditional "set status freely" affordance.
|
||||
var result = parsed == TaskStatus.Queued
|
||||
|
||||
Reference in New Issue
Block a user