feat(dashboard): cancel queued tasks from UI + /cancel drops queue entries (#302) #306
No reviewers
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!306
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/302-cancel-queued-tasks"
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?
Closes #302.
Summary
Worker.dropQueuedById(taskId)— removes a queued entry by id, returns it for the caller to persist + broadcast (ornullif unknown / already running)./cancelextended: whentask_iddoesn't match any running worker, scan every worker's queue and drop the first match. Returns{ status: "dropped-from-queue", task_id, agent, issue_number }. The 404 message updates to "no running or queued task with id".task_cancelledSSE envelope emitted on both paths (reason: "operator-aborted"for running,"operator-dropped"for queued) so the dashboard can pop the row without a full refetch.task-detail.tsx— Cancel button now renders forstatus === "queued"too, with differentiated copy ("✕ Drop from queue" / "Confirm drop" / "Dropping…") and a matching toast.Design notes
dropQueuedByIddeliberately does not resolve the pending promise registered byenqueue.TaskResult.statusis"success" | "failure" | "cost_capped"— no"cancelled"variant — so a synthetic resolve would either mislabel the outcome or require widening the union across every consumer. The existingassign-deduppath inwebhook-handlers.ts:206uses the same splice-without-resolve shape.TaskRecord.statusis the operator-visible source of truth and the handler flips it to"cancelled"+ persists.dropQueuedByIdprimitive is intentionally narrow so #301 (webhookissues.unassigned) can land against the same method without a second surface.Test plan
worker.test.ts—dropQueuedByIdremoves the right entry, returnsnullon unknown id, returnsnullwhen the task has already promoted tocurrentTask.main.test.ts— new/cancelscenarios: queued drop + status flip, 404 message update, running-first precedence when atask_idcould match both.task-detail.test.tsx— existing tests still pass (no test-level regressions from the widened gate).bun x biome checkclean./cancelwith its id, confirm dashboard row disappears via the SSE event.Out of scope (tracked elsewhere)
issues.unassignedhandler — #301, lands against the samedropQueuedByIdprimitive.🤖 Generated with Claude Code
Extend /cancel to accept a task_id that points at a queued entry — scans each worker's `queue[]`, removes the match via a new `Worker.dropQueuedById`, flips the TaskRecord to cancelled, persists, and broadcasts a `task_cancelled` SSE envelope (also emitted on the existing running-abort path for symmetry). UI: Cancel button gated on `status === "running" || status === "queued"` with copy differentiated ("Drop from queue" / "Confirm drop" vs. Cancel) and a toast that mirrors the backend `dropped-from-queue` status. Shares the `dropQueuedById` primitive with the planned `issues.unassigned` webhook handler (#301) — both land against one worker method so the queue-drop shape stays narrow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>