feat(tasks): add coalescing queue and task lifecycle guards

- Coalescing queue: use HasPendingTaskForIssue (queued/dispatched only)
  instead of HasActiveTaskForIssue so comments during a running task
  enqueue exactly one follow-up task that picks up all new comments.
- Stale task cleanup: runtime sweeper now fails orphaned tasks when
  their runtime goes offline (daemon crash/network partition).
- Cancel-aware daemon: handleTask checks task status after execution
  and discards results if the task was cancelled mid-run (e.g. reassign).
- Terminal issue guard: ClaimTaskForRuntime auto-cancels pending tasks
  for done/cancelled issues instead of executing them.
- Race condition safety net: unique partial index ensures at most one
  pending task per issue at the DB level.
This commit is contained in:
Jiayuan 2026-03-29 17:52:35 +08:00
parent 32f795e1ef
commit b112d1f1ae
13 changed files with 148 additions and 3 deletions

View file

@ -102,6 +102,14 @@ WHERE agent_id = $1 AND status IN ('dispatched', 'running');
SELECT count(*) > 0 AS has_active FROM agent_task_queue
WHERE issue_id = $1 AND status IN ('queued', 'dispatched', 'running');
-- name: HasPendingTaskForIssue :one
-- Returns true if there is a queued or dispatched (but not yet running) task for the issue.
-- Used by the coalescing queue: allow enqueue when a task is running (so
-- the agent picks up new comments on the next cycle) but skip if a pending
-- task already exists (natural dedup).
SELECT count(*) > 0 AS has_pending FROM agent_task_queue
WHERE issue_id = $1 AND status IN ('queued', 'dispatched');
-- name: ListPendingTasksByRuntime :many
SELECT * FROM agent_task_queue
WHERE runtime_id = $1 AND status IN ('queued', 'dispatched')