feat(dashboard): cancel queued tasks from the UI + extend /cancel to drop queued entries #302
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks#302
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
User story
As an operator watching the queue, I want to cancel a queued task (one that has not started running yet) directly from the dashboard, so that I don't have to wait for it to promote into "running" just to hit Cancel — and so that mis-dispatched work never burns a worker slot.
Current behaviour
apps/server/src/main.ts:685(handleCancel) filters overbusyWorkers— workers with acurrentAbort+currentTask. A task sitting inworker.queued[]has no abort handle and is invisible to/cancel. The UI mirrors this:apps/web/src/components/task-detail.tsx:109only renders the Cancel button whenstatus === "running". Queued rows have no affordance.Acceptance criteria
Backend
/cancelto accept a queued-task identifier:POST /cancel { "task_id": "<uuid>" }— whentask_idmatches a queued entry (scan each worker'squeued[]), drop it from the queue, mark the persistedTaskRecordascancelled, return{ status: "dropped-from-queue", task_id, agent }.task_idmatches a running task.task_idmatches neither running nor any queued.dropQueuedById(taskId: string): TaskRecord | nullmethod. Shared with the unassign-handler sibling ticket — land both against the same primitive.task_cancelledwith{ task_id, agent, reason: "operator-dropped" | "operator-aborted" }so the dashboard can pop the row without a full refetch.UI
task-detail.tsx: render the Cancel button whenstatus === "queued"as well, with copy "Drop from queue" vs. "Cancel" for running. Same confirmation flow; on success, refetch the task detail.abc12345from boss-2 queue" vs. "Cancelledabc12345on boss-2".Tests
main.test.ts→POST /cancelunit tests: new cases fortask_idpointing at a queued entry (drops + persists cancellation), at an unknown id (404), and at a running task (unchanged behaviour).worker.test.ts:dropQueuedByIdremoves the entry, persists the record, returns the dropped row; no-op for unknown id.task-detail.test.tsx: Cancel button renders forqueuedstatus; click posts/cancelwith the right body.Out of scope
/cancel { agent }should eventually do — scope it to a follow-up, current handler semantics are per-running-task).References
apps/server/src/main.ts:685—handleCancelto extendapps/server/src/background/worker.ts— queue internals, shared with siblingapps/web/src/components/task-detail.tsx:109— UI gate to widenissues.unassignedhandler (reuses the samedropQueued*primitive)