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:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user