feat(state): advance WaitingForChildren parent to review when children terminal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-06-04 15:39:24 +02:00
parent 6f4b5d5544
commit 7873e60095

View File

@@ -392,5 +392,56 @@ public sealed class TaskStateService : ITaskStateService
{ {
_logger.LogWarning(ex, "TryCompleteParent failed for {ParentId}", parentId); _logger.LogWarning(ex, "TryCompleteParent failed for {ParentId}", parentId);
} }
try
{
await TryAdvanceImprovementParentAsync(parentId);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "TryAdvanceImprovementParent failed for {ParentId}", parentId);
}
}
// Improvement parents sit in WaitingForChildren while their suggested children run.
// Once every child is terminal (Done/Failed/Cancelled) the parent surfaces for review;
// a failed or cancelled child does not wedge the parent — it is flagged on the result.
private async Task TryAdvanceImprovementParentAsync(string parentId)
{
string? parentResult;
List<TaskStatus> childStatuses;
await using (var ctx = await _dbFactory.CreateDbContextAsync(CancellationToken.None))
{
var parent = await ctx.Tasks.AsNoTracking()
.FirstOrDefaultAsync(t => t.Id == parentId, CancellationToken.None);
if (parent is null || parent.Status != TaskStatus.WaitingForChildren) return;
parentResult = parent.Result;
childStatuses = await ctx.Tasks
.Where(t => t.ParentTaskId == parentId)
.Select(t => t.Status)
.ToListAsync(CancellationToken.None);
}
if (childStatuses.Count == 0) return;
bool allTerminal = childStatuses.All(s =>
s == TaskStatus.Done || s == TaskStatus.Failed || s == TaskStatus.Cancelled);
if (!allTerminal) return;
int failed = childStatuses.Count(s => s == TaskStatus.Failed);
int cancelled = childStatuses.Count(s => s == TaskStatus.Cancelled);
var newResult = parentResult;
if (failed + cancelled > 0)
{
var note = $"⚠ Children: {failed} failed, {cancelled} cancelled.";
newResult = string.IsNullOrWhiteSpace(parentResult) ? note : $"{parentResult}\n\n{note}";
}
await using var writeCtx = await _dbFactory.CreateDbContextAsync(CancellationToken.None);
await writeCtx.Tasks
.Where(t => t.Id == parentId && t.Status == TaskStatus.WaitingForChildren)
.ExecuteUpdateAsync(s => s
.SetProperty(t => t.Status, TaskStatus.WaitingForReview)
.SetProperty(t => t.Result, newResult), CancellationToken.None);
await _broadcaster.TaskUpdated(parentId);
} }
} }