feat(monitor): one-click re-dispatch for failed / cancelled / interrupted tasks #222
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#222
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, I want a Re-dispatch button on every task-history row whose status is terminal-but-not-successful (
failed,cancelled,interrupted), so that recovering from an aborted run is a single click instead of the Forgejo unassign → reassign dance I had to do for #209 / #210 on 2026-04-21.Acceptance criteria
Server
POST /task/:id/redispatch(auth-gated via the M18-8 middleware). Reads the originaltask_historyrow, reusesrepo+issue_number+agent_type+user, generates a freshtask_id, and enqueues it through the same pool scheduler path anissues.assignedwebhook would use. Returns202 { task_id }on success;409 Conflictif the original is stillrunning;404if thetask_idis unknown.sessions.jsonstill has an entry for the<type>:<repo>:<issue>key — same resume semantics as a natural re-dispatch.UI
/app/monitor/tasks(the Tasks table) — a ↻ Re-dispatch icon button appears on rows withstatus∈ {failed,cancelled,interrupted}. Button is disabled (with a tooltip "task is running") when the row's latest status isrunning./app/monitor/task/:taskId(the task detail view) — same button in the top-right actions bar.POST /task/:id/redispatch, shows a toast on success with a link to the new task detail, rolls back the optimistic state on non-2xx.Safety rails
410 Gonewith a body hint to reopen the issue first. Avoids spawning a worker on stale state.interruptedtask that still has uncommitted worktree content is fine — the worker's existing "worktree has uncommitted changes from a previous dispatch" warning covers it.Verification
apps/server/src/main.test.tsfor the new endpoint.apps/web/e2e/monitor.spec.ts— mock afailedtask row, click the re-dispatch button, assert POST fires and a toast appears./cancel, click Re-dispatch from Monitor, confirm a new dispatch fires (check service logs for[dev-*] starting <new task id>).Out of scope
References
apps/server/src/task-store.ts—TaskRecordshape;listTasksForIssuealready fetches by issue.apps/server/src/pool.ts::dispatchByType— the path a new dispatch should take.apps/server/src/webhook-handlers.ts::handleIssueAssigned— template for how to build the task envelope from an issue + type.