feat(worker): surface task numbers in MCP tool return payloads

Adds Number alongside every task id in External/'s DTOs -- the two
central mappers (ToDto/ToRefDto -> TaskDto/TaskRefDto) plus every
DTO that carries a bare task id and bypasses them (batch results,
queue state, wait-for-change, config, attachments, handoff,
lifecycle, merge-preview-set, worktree list). Input resolution
(#123 as an argument) stays for slice 3.
This commit is contained in:
mika kuns
2026-08-11 11:59:51 +02:00
parent 8da1a12389
commit 106c964410
12 changed files with 129 additions and 64 deletions
+14 -12
View File
@@ -29,6 +29,7 @@ public sealed record BatchGetTaskResult(string Id, bool Found, TaskRefDto? Task,
// run reported, without pulling in the rest of Result/Description to get them.
public sealed record BatchTaskDetailDto(
string Id,
int Number,
string? ListId = null,
string? Title = null,
string? Description = null,
@@ -51,9 +52,9 @@ public sealed record BatchAddTaskResult(
IReadOnlyList<PossibleDuplicateDto>? PossibleDuplicates, string? Error);
// BaseDirty mirrors TaskRefDto.BaseDirty: populated only for BatchUpdateTaskStatus items that
// just transitioned to Queued against a list whose working dir has uncommitted changes.
public sealed record BatchTaskResult(string TaskId, bool Ok, string? Error, DirtyBaseWarning? BaseDirty = null);
public sealed record BatchCancelResult(string TaskId, bool Ok, bool Cancelled, string? Error);
public sealed record BatchCleanupResult(string TaskId, bool Ok, bool Removed, bool BranchDeleted, string? Error);
public sealed record BatchTaskResult(string TaskId, bool Ok, string? Error, DirtyBaseWarning? BaseDirty = null, int? Number = null);
public sealed record BatchCancelResult(string TaskId, bool Ok, bool Cancelled, string? Error, int? Number = null);
public sealed record BatchCleanupResult(string TaskId, bool Ok, bool Removed, bool BranchDeleted, string? Error, int? Number = null);
/// <summary>
/// Batch variants of the single-entity tools on <see cref="ExternalMcpService"/>.
@@ -166,6 +167,7 @@ public sealed class BatchMcpTools
return new BatchTaskDetailDto(
Id: t.Id,
Number: t.Number,
ListId: Want("listId") ? t.ListId : null,
Title: Want("title") ? t.Title : null,
Description: description,
@@ -252,7 +254,7 @@ public sealed class BatchMcpTools
try
{
var task = await _svc.UpdateTaskStatus(id, status, cancellationToken);
results.Add(new BatchTaskResult(id, true, null, task.BaseDirty));
results.Add(new BatchTaskResult(id, true, null, task.BaseDirty, task.Number));
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
@@ -277,7 +279,7 @@ public sealed class BatchMcpTools
try
{
var r = await _svc.CancelTask(id, cancellationToken);
results.Add(new BatchCancelResult(id, true, r.Cancelled, null));
results.Add(new BatchCancelResult(id, true, r.Cancelled, null, r.Number));
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
@@ -296,7 +298,7 @@ public sealed class BatchMcpTools
{
EnsureWithinCap(taskIds, nameof(taskIds));
return await RunPerTaskAsync(taskIds,
(id, ct) => _svc.DeleteTask(id, ct), cancellationToken);
async (id, ct) => (await _svc.DeleteTask(id, ct)).Number, cancellationToken);
}
[McpServerTool, Description(
@@ -313,8 +315,8 @@ public sealed class BatchMcpTools
{
try
{
await _svc.SetMyDay(item.TaskId, item.IsMyDay, item.SortOrder, cancellationToken);
results.Add(new BatchTaskResult(item.TaskId, true, null));
var task = await _svc.SetMyDay(item.TaskId, item.IsMyDay, item.SortOrder, cancellationToken);
results.Add(new BatchTaskResult(item.TaskId, true, null, Number: task.Number));
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
@@ -342,7 +344,7 @@ public sealed class BatchMcpTools
try
{
var r = await _svc.CleanupTaskWorktree(id, force, cancellationToken);
results.Add(new BatchCleanupResult(id, true, r.Removed, r.BranchDeleted, null));
results.Add(new BatchCleanupResult(id, true, r.Removed, r.BranchDeleted, null, r.Number));
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
@@ -354,15 +356,15 @@ public sealed class BatchMcpTools
}
private static async Task<IReadOnlyList<BatchTaskResult>> RunPerTaskAsync(
string[] taskIds, Func<string, CancellationToken, Task> op, CancellationToken cancellationToken)
string[] taskIds, Func<string, CancellationToken, Task<int?>> op, CancellationToken cancellationToken)
{
var results = new List<BatchTaskResult>(taskIds.Length);
foreach (var id in taskIds)
{
try
{
await op(id, cancellationToken);
results.Add(new BatchTaskResult(id, true, null));
var number = await op(id, cancellationToken);
results.Add(new BatchTaskResult(id, true, null, Number: number));
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)