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:
@@ -115,22 +115,16 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
catch { /* best-effort queue refresh */ }
|
||||
}
|
||||
|
||||
// Drop-to-queue: a task dragged from the main app onto Mission Control gets queued.
|
||||
// Drop-to-queue: a task dragged from the main app onto Mission Control gets queued. Goes
|
||||
// through the worker hub (TaskStateService.EnqueueAsync) rather than a raw EF write so the
|
||||
// manual/draft-child guards apply here too. The interactive-session check has to live here
|
||||
// rather than on the worker side: the worker never touches task status for a UI-hosted ConPTY
|
||||
// session, so it has no way to know one is open — only Mission Control's own pane list does.
|
||||
public async System.Threading.Tasks.Task EnqueueTaskAsync(string taskId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(taskId)) return;
|
||||
try
|
||||
{
|
||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var entity = await db.Tasks.FirstOrDefaultAsync(t => t.Id == taskId);
|
||||
if (entity is null
|
||||
|| entity.Status == ClaudeDo.Data.Models.TaskStatus.Running
|
||||
|| entity.Status == ClaudeDo.Data.Models.TaskStatus.Queued)
|
||||
return;
|
||||
entity.Status = ClaudeDo.Data.Models.TaskStatus.Queued;
|
||||
await db.SaveChangesAsync();
|
||||
await _worker.WakeQueueAsync();
|
||||
}
|
||||
if (ConPtySessions.Any(s => s.TaskId == taskId)) return;
|
||||
try { await _worker.SetTaskStatusAsync(taskId, ClaudeDo.Data.Models.TaskStatus.Queued); }
|
||||
catch { /* best-effort enqueue */ }
|
||||
await RefreshQueueAsync();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user