Merge branch 'claudedo/9a02814991ef42a5a9a063d5ea579d62'

This commit is contained in:
Mika Kuns
2026-09-02 13:49:56 +02:00
9 changed files with 1181 additions and 11 deletions
+20 -6
View File
@@ -62,7 +62,8 @@ public sealed record TaskDto(
// planning-chain predecessor or DependsOnTaskId hasn't reached Done. See BlockedReason.
bool Blocked = false,
string? BlockedReason = null,
bool IsManual = false);
bool IsManual = false,
bool ExpectsNoDiff = false);
// 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.
@@ -85,7 +86,8 @@ public sealed record TaskRefDto(
bool Blocked = false,
string? BlockedReason = null,
DirtyBaseWarning? BaseDirty = null,
bool IsManual = false);
bool IsManual = false,
bool ExpectsNoDiff = false);
// tasks is populated when includeDescription=false (the default): lean references, no
// Description/Result. tasksFull is populated when includeDescription=true: full tasks incl.
@@ -300,6 +302,10 @@ public sealed class ExternalMcpService
"dependency cycle. If that predecessor instead ends up Failed or Cancelled, this task simply " +
"stays blocked rather than starving silently -- check get_task/list_tasks' blocked field.")]
string? dependsOnTaskId = null,
[Description("true: this task's correct outcome may be an empty diff (verification/audit/research work " +
"that doesn't change code) — the merge helper won't treat a 0-changed-files result as a red " +
"flag. false (default): an empty diff is treated as suspicious, not a clean success.")]
bool expectsNoDiff = false,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(listId))
@@ -327,6 +333,7 @@ public sealed class ExternalMcpService
Model = ModelRegistry.NormalizeAlias(model),
// A manual list holds reminders, so anything filed there starts out manual.
IsManual = list.IsManual,
ExpectsNoDiff = expectsNoDiff,
};
await _tasks.AddAsync(entity, cancellationToken);
@@ -422,8 +429,8 @@ public sealed class ExternalMcpService
}
[McpServerTool, Description(
"Update an existing task's title, description, commit type, dependsOn link, and/or manual flag. Pass " +
"null to leave a field unchanged." + McpToolDocs.NotWhileRunning + McpToolDocs.LeanTaskRef)]
"Update an existing task's title, description, commit type, dependsOn link, and/or manual/expects-no-diff " +
"flags. Pass null to leave a field unchanged." + McpToolDocs.NotWhileRunning + McpToolDocs.LeanTaskRef)]
public async Task<TaskRefDto> UpdateTask(
string taskId,
string? title = null,
@@ -437,6 +444,10 @@ public sealed class ExternalMcpService
"daily prep, and the list handler all skip it (a ConPTY session may still touch it). false: " +
"clear the flag. null (default): leave unchanged.")]
bool? isManual = null,
[Description("true: this task's correct outcome may be an empty diff (verification/audit/research work " +
"that doesn't change code) — the merge helper won't treat a 0-changed-files result as a red " +
"flag. false: clear the flag. null (default): leave unchanged.")]
bool? expectsNoDiff = null,
CancellationToken cancellationToken = default)
{
taskId = await TaskIdResolver.ResolveAsync(_tasks, taskId, cancellationToken);
@@ -449,6 +460,7 @@ public sealed class ExternalMcpService
if (description is not null) task.Description = description;
if (commitType is not null) task.CommitType = commitType;
if (isManual is not null) task.IsManual = isManual.Value;
if (expectsNoDiff is not null) task.ExpectsNoDiff = expectsNoDiff.Value;
await _tasks.UpdateAsync(task, cancellationToken);
if (dependsOnTaskId is not null)
@@ -986,7 +998,8 @@ public sealed class ExternalMcpService
t.DependsOnTaskId,
blocked,
blockedReason,
t.IsManual);
t.IsManual,
t.ExpectsNoDiff);
// internal: AbortMerge in MergeMcpTools also needs this to build its TaskRefDto result.
internal static TaskRefDto ToRefDto(TaskEntity t, bool blocked = false, string? blockedReason = null) => new(
@@ -1005,7 +1018,8 @@ public sealed class ExternalMcpService
blocked,
blockedReason,
BaseDirty: null,
IsManual: t.IsManual);
IsManual: t.IsManual,
ExpectsNoDiff: t.ExpectsNoDiff);
// "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.