Merge branch 'claudedo/16172942c1424a67a0499e01cefb7d60'

This commit is contained in:
mika kuns
2026-08-10 15:08:51 +02:00
19 changed files with 1295 additions and 24 deletions
+2 -1
View File
@@ -83,7 +83,8 @@ public sealed class BatchMcpTools
[McpServerTool, Description(
"Fetch a snapshot of many tasks in one call — use for an overview or polling a fan-out instead of " +
"calling get_task per id. A missing id comes back as found=false, not an error; error is only set " +
"for an unexpected failure. When includeDescription=true, Description/Result are cut to " +
"for an unexpected failure. A Failed task's task/taskFull carries failureReason (see get_task). " +
"When includeDescription=true, Description/Result are cut to " +
"descriptionMaxChars (default 1500 — was unlimited); check *Truncated/*FullLength before assuming " +
"you got the whole text. Use `fields` to fetch only what you need (e.g. just roadblockText) instead " +
"of raising the cap. The whole response is still capped — if it's too big even so, lower " +
+26 -5
View File
@@ -45,7 +45,13 @@ public sealed record TaskDto(
// A planning/improvement child reporting > 0 still goes straight to Done (see
// ClaudeDo.Worker/CLAUDE.md → Unified parent model) -- this is the only MCP-visible signal
// that it may have delivered nothing despite that Done status.
int RoadblockCount = 0);
int RoadblockCount = 0,
// The three below are non-null only when Status=Failed; stamped by TaskRunner.MarkFailed via
// TaskStateService.FailAsync. failureReason is "unknown" for a Failed task that predates this
// field. Lets a caller triage without pulling get_task_log's raw NDJSON.
string? FailureReason = null,
int? FailureTurnsUsed = null,
int? FailureMaxTurns = null);
// Lean counterpart to TaskDto for writing/status-changing tools: echoes back what changed
// without re-sending Description/Result, which the caller just sent or already has.
@@ -56,7 +62,10 @@ public sealed record TaskRefDto(
string Status,
int SortOrder,
bool IsMyDay,
int RoadblockCount = 0);
int RoadblockCount = 0,
string? FailureReason = null,
int? FailureTurnsUsed = null,
int? FailureMaxTurns = null);
// tasks is populated when includeDescription=false (the default): lean references, no
// Description/Result. tasksFull is populated when includeDescription=true: full tasks incl.
@@ -236,7 +245,8 @@ public sealed class ExternalMcpService
"A successful run lands in WaitingForReview; use review_task to approve, reject or cancel it. " +
"Done/Failed/Cancelled tasks can be reset to Idle for re-execution. A Queued task with a blocker waits " +
"for its predecessor before the picker will claim it, and WaitingForChildren is a parent whose own work " +
"is done but whose children are still running.")]
"is done but whose children are still running. For Status=Failed, failureReason (max_turns|timeout|" +
"error|cancelled|unknown) plus failureTurnsUsed/failureMaxTurns say why without pulling get_task_log.")]
public async Task<TaskDto> GetTask(string taskId, CancellationToken cancellationToken)
{
var task = await _tasks.GetByIdAsync(taskId, cancellationToken)
@@ -1500,7 +1510,10 @@ public sealed class ExternalMcpService
t.FinishedAt,
t.IsMyDay,
t.SortOrder,
t.RoadblockCount);
t.RoadblockCount,
FailureReasonOf(t),
t.Status == TaskStatus.Failed ? t.FailureTurnsUsed : null,
t.Status == TaskStatus.Failed ? t.FailureMaxTurns : null);
private static TaskRefDto ToRefDto(TaskEntity t) => new(
t.Id,
@@ -1509,7 +1522,15 @@ public sealed class ExternalMcpService
t.Status.ToString(),
t.SortOrder,
t.IsMyDay,
t.RoadblockCount);
t.RoadblockCount,
FailureReasonOf(t),
t.Status == TaskStatus.Failed ? t.FailureTurnsUsed : null,
t.Status == TaskStatus.Failed ? t.FailureMaxTurns : null);
// "unknown" covers a Failed task that predates this field (never got a classified reason
// stamped) — a defined value rather than null so callers don't have to special-case it.
private static string? FailureReasonOf(TaskEntity t) =>
t.Status == TaskStatus.Failed ? (t.FailureReason ?? "unknown") : null;
}
internal static class DailyPrepFilter
+6 -1
View File
@@ -20,7 +20,12 @@ public sealed class LifecycleMcpTools
_reset = reset;
}
[McpServerTool, Description("Reset a failed task back to Idle so it can be run again, discarding its now-stale worktree. Only tasks with Status=Failed are accepted; other statuses throw.")]
[McpServerTool, Description(
"Discards a failed task's worktree (and all uncommitted work in it) and resets it to Idle for a fresh " +
"run. Only tasks with Status=Failed are accepted; other statuses throw. Check failureReason from " +
"get_task/batch_get_tasks first: for failureReason=\"max_turns\" the worktree's work is usually still " +
"good and continue_task (resume the session, keep the worktree) is almost always the right call instead " +
"— reach for this tool only for a real error, not a task that just ran out of turns.")]
public async Task<ResetFailedTaskResult> ResetFailedTask(string taskId, CancellationToken cancellationToken)
{
var task = await _tasks.GetByIdAsync(taskId, cancellationToken)