fix(ui): gate queueing on an open interactive ConPTY session
A task-based ConPTY session leaves the row Idle in the DB (sessions never touch status), so nothing stopped the queue picker from claiming it too: CanSendToQueue ignored HasInteractiveSession, and both TasksIslandViewModel. SendToQueueAsync and MissionControlViewModel.EnqueueTaskAsync (drag-to-queue) wrote Status=Queued straight via EF, bypassing TaskStateService entirely and its manual/draft-child guards. That let an autonomous claude process spawn in the same worktree a user was hand-editing in the ConPTY pane. Add !HasInteractiveSession to CanSendToQueue, and route both UI enqueue paths through IWorkerClient.SetTaskStatusAsync (worker hub -> TaskStateService. EnqueueAsync) instead of raw EF writes. The interactive-session gate itself stays in the UI: the worker has no notion of a UI-hosted ConPTY pane.
This commit is contained in:
@@ -833,26 +833,18 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
|
||||
[RelayCommand]
|
||||
private async Task SendToQueueAsync(TaskRowViewModel? row)
|
||||
{
|
||||
if (row is null || row.IsRunning) return;
|
||||
if (row is null || row.IsRunning || row.HasInteractiveSession || _worker is null) return;
|
||||
// A finalized planning parent queues its plan (children sequentially), not itself.
|
||||
if (row.CanQueuePlan)
|
||||
{
|
||||
await QueuePlanningSubtasksAsync(row);
|
||||
return;
|
||||
}
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == row.Id);
|
||||
if (entity is null) return;
|
||||
entity.Status = TaskStatus.Queued;
|
||||
await db.SaveChangesAsync();
|
||||
row.Status = TaskStatus.Queued;
|
||||
if (_worker is not null)
|
||||
{
|
||||
try { await _worker.WakeQueueAsync(); } catch { }
|
||||
}
|
||||
Regroup();
|
||||
UpdateSubtitle();
|
||||
TasksChanged?.Invoke(this, EventArgs.Empty);
|
||||
// Goes through the worker hub (TaskStateService.EnqueueAsync) rather than a raw EF write
|
||||
// so the manual/draft-child guards apply here too; the row refreshes from the resulting
|
||||
// TaskUpdated broadcast.
|
||||
try { await _worker.SetTaskStatusAsync(row.Id, TaskStatus.Queued); }
|
||||
catch (Exception ex) { ErrorReported?.Invoke(Loc.T("vm.tasksIsland.sendToQueueFailed", ex.Message)); }
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
Reference in New Issue
Block a user