Force-clear / kill stuck board card from the UI #609
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#609
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 planner board, I want a one-click "kill / force-clear" action on a stuck card, so that I can manually unblock workflow when the automatic dispatch chain has gotten wedged (e.g. a
changes_requestedreview that never triggered an address-review dispatch, leaving the card idle with a successful prior task that/task/:id/redispatchrefuses to re-run).Background
Concrete situation that motivated this ticket —
charles/claude-hooks#567/ PR #604 on 2026-04-30:status=success~12 min before this ticket (only row intask_historyfor that issue).address-reviewdispatch was enqueued —task_historystill shows just the onesuccessrow, so the review event silently failed to fan out (separate bug — see companion ticket).IN REVIEWcolumn with stall age climbing. Existing UI affordances are dead-ends:failure/cancelled/interrupted(apps/server/src/main.ts:1266); asuccesstask returns 409.card.status === "running"(apps/web/src/components/board/board-side-panel.tsx:222); idle cards do not show it.The current escape hatches are all off the dashboard: dismiss + re-request the review on Forgejo, or unassign+reassign on Forgejo, or hand-craft a
POST /task. None are operator-friendly.Acceptance criteria
Server-side endpoint
POST /board/kick(or extendPOST /task/:id/redispatchwith aforce=trueflag — pick one, document the choice in the handler docstring).{ repo: string, issue_number: number }. Resolves the assigned agent type from the latesttask_historyrow (or current Forgejo assignee), rebuilds the same task prompt the regularissues.assignedflow would have built, and enqueues it through the normal pool scheduler.status === "success"409 gate and the running/queued duplicate gate (those gates exist for the redispatch path and are exactly what blocks the operator here).🦵 Operator kicked the queue — re-running address-review.202 { task_id }on success; clear error codes for unknown issue, no agent token available, etc.<type>:<repo>:<issue>, the new task picks up from there (mirrors/task/:id/redispatch).Skill selection
changes_requestedoutstanding, dispatch theaddress-reviewskill (the same oneevent-handlers.ts:347would have picked).implement(matches the post-reroute behaviour).UI
apps/web/src/components/board/board-side-panel.tsx), regardless ofstatus. Toneerror, leadingIcon something likeZaporRotateCwfromlucide-reactperapps/web/CLAUDE.md.<repo>#<n>? A fresh task will be enqueued even if the previous one succeeded."Kicked → task <id8>, invalidate["board"]+["history"]query keys.isPending).<Button>primitive.Tests
successtask (the case redispatch refuses),address-reviewselection when outstandingchanges_requestedreview,implementselection otherwise, missing token / unknown issue error paths.Audit + observability
task_historyfor the kicked dispatch (the regular dispatch path already does this; just verify the marker /agent_typeis right).Out of scope
changes_requested) — covered in the companion ticketInvestigate: address-review did not dispatch for PR #604 / issue #567.References
apps/server/src/main.ts:1232—handleTaskRedispatch(the gate that blocks the operator today)apps/server/src/main.ts:1266— thestatus === "success"409 pathapps/server/src/domain/views/board.ts:1097—handleBoardReroute(closest existing pattern; reuse the rate-limit + audit comment patterns)apps/server/src/domain/workflow/event-handlers.ts:338-347— pivot logic forimplement → address-reviewwhen PR has outstandingchanges_requestedapps/server/src/domain/workflow/review-loop.ts— review-loop cap (relevant for kick safety)apps/web/src/components/board/board-side-panel.tsx:115-233— current side-panel button gatingapps/web/src/routes/planner.board.tsx:486— wiring of cancel/reroute mutationsapps/web/CLAUDE.md— foundation primitives +<Button>+ Lucide icon rulesresultevent #646