Merge branch 'claudedo/c209d54cef5642278673d41e8b98fc43'

This commit is contained in:
mika kuns
2026-09-02 22:38:42 +02:00
6 changed files with 48 additions and 2 deletions
@@ -112,6 +112,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
[NotifyPropertyChangedFor(nameof(IsQueued))]
[NotifyPropertyChangedFor(nameof(IsWaiting))]
[NotifyPropertyChangedFor(nameof(StatusChipClass))]
[NotifyPropertyChangedFor(nameof(CanRemoveFromQueue))]
private string? _blockedByTaskId;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(DependsOnTaskId))]
@@ -276,7 +277,7 @@ public sealed partial class TaskRowViewModel : ViewModelBase
&& !HasInteractiveSession;
public bool IsQueued => Status == TaskStatus.Queued && string.IsNullOrEmpty(BlockedByTaskId);
public bool IsWaiting => Status == TaskStatus.Queued && !string.IsNullOrEmpty(BlockedByTaskId);
public bool CanRemoveFromQueue => IsQueued || HasQueuedSubtasks;
public bool CanRemoveFromQueue => IsQueued || IsWaiting || HasQueuedSubtasks;
// "Send to queue" is the single queue entry. On a finalized planning parent it queues the
// plan (children) via CanQueuePlan; an Active (not-yet-finalized) planning parent is hidden —
// it must be finalized first. The worker never sees a UI-hosted ConPTY session (it never
@@ -430,6 +430,17 @@ public sealed class TaskStateService : ITaskStateService
if (affected == 0)
return new TransitionResult(false, "Task is not queued; cannot remove from queue.");
// A dequeued task may have been a planning-chain predecessor. Without unblocking its
// successor here, the successor is left permanently wedged on a BlockedByTaskId link to
// a task that has left the queue and will never reach the terminal state that would
// otherwise resolve it (PlanningChainCoordinator.OnChildFinishedAsync).
var successorId = await ctx.Tasks.AsNoTracking()
.Where(t => t.BlockedByTaskId == taskId)
.Select(t => t.Id)
.FirstOrDefaultAsync(ct);
if (successorId is not null)
await UnblockAsync(successorId, ct);
await NotifyAsync(taskId);
return new TransitionResult(true, null);
}