feat(mcp): allow Done via update_task_status with worktree guard
External tasks finished outside a ClaudeDo run had no way to close out their tracking task; update_task_status now permits Done alongside Idle/Queued/Cancelled, refusing it when the task has an active worktree so review_task stays the only path that merges.
This commit is contained in:
+2
-2
@@ -93,8 +93,8 @@ public sealed class BatchMcpTools
|
||||
}
|
||||
|
||||
[McpServerTool, Description(
|
||||
"Set the status of many tasks at once. status is 'Idle' (reset to editable) or " +
|
||||
"'Queued' (enqueue for execution) only — same rule as update_task_status. " +
|
||||
"Set the status of many tasks at once. status is 'Idle', 'Queued', 'Cancelled' or 'Done' only — " +
|
||||
"same rule as update_task_status ('Done' is refused per-item for a task with an active worktree). " +
|
||||
"Returns one result per id: { taskId, ok, error }. Max 100 ids.")]
|
||||
public async Task<IReadOnlyList<BatchTaskResult>> BatchUpdateTaskStatus(
|
||||
string[] taskIds, string status, CancellationToken cancellationToken)
|
||||
|
||||
+18
-2
@@ -264,10 +264,12 @@ public sealed class ExternalMcpService
|
||||
}
|
||||
|
||||
[McpServerTool, Description(
|
||||
"Update a task's status. Only 'Idle', 'Queued' and 'Cancelled' are permitted externally — " +
|
||||
"Update a task's status. Only 'Idle', 'Queued', 'Cancelled' and 'Done' are permitted externally — " +
|
||||
"use run_task_now for execution control, and review_task to act on a WaitingForReview task. " +
|
||||
"Settable: Idle (reset to editable), Queued (enqueue for execution), " +
|
||||
"Cancelled (retire the task without deleting it; it can be reset to Idle later). " +
|
||||
"Cancelled (retire the task without deleting it; it can be reset to Idle later), " +
|
||||
"Done (mark complete; refused if the task has an active worktree — use review_task to approve " +
|
||||
"and merge that worktree instead). " +
|
||||
"Full lifecycle: Idle → Queued → Running → WaitingForReview → Done | Failed | Cancelled.")]
|
||||
public async Task<TaskDto> UpdateTaskStatus(
|
||||
string taskId,
|
||||
@@ -300,6 +302,20 @@ public sealed class ExternalMcpService
|
||||
throw new InvalidOperationException(cancelResult.Reason ?? "Cannot cancel task.");
|
||||
break;
|
||||
|
||||
case TaskStatus.Done:
|
||||
await using (var ctx = await _dbFactory.CreateDbContextAsync(cancellationToken))
|
||||
{
|
||||
var wt = await new WorktreeRepository(ctx).GetByTaskIdAsync(taskId, cancellationToken);
|
||||
if (wt is not null && wt.State == WorktreeState.Active)
|
||||
throw new InvalidOperationException(
|
||||
"Task has an active worktree — use review_task to approve and merge instead.");
|
||||
}
|
||||
|
||||
var doneResult = await _state.ForceSetStatusAsync(taskId, TaskStatus.Done, cancellationToken);
|
||||
if (!doneResult.Ok)
|
||||
throw new InvalidOperationException(doneResult.Reason ?? "Cannot set task to Done.");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException(
|
||||
$"Status '{target}' is not settable externally. Use run_task_now or review_task.");
|
||||
|
||||
Reference in New Issue
Block a user