Current state
When dispatch_inner fails to create a workspace or spawn a tmux window after claim_for_dispatch has already set the task to in-progress, the task is left permanently stuck in in-progress with no running agent attached and no automated recovery path. A developer must manually intervene in SQLite to reset the task status, or the task is silently abandoned — a ghost entry that occupies the in-progress slot indefinitely.
Ideal state
- A failure in
ensure_task_worktree or the tmux spawn after claim_for_dispatch succeeds rolls the task status back to ready (or records a failed status with an error message).
- No task is ever left permanently in
in-progress with no running agent attached.
- The task's status accurately reflects whether an agent session is actually running against it.
Out of scope
- Retry logic for transient workspace or tmux creation failures.
- Changes to the
claim_for_dispatch semantics or the happy path.
Starting points
workflows/src/dispatch.rs — dispatch_inner: claim_for_dispatch is called around line 445; ensure_task_worktree and the tmux spawn follow with no compensating rollback on failure.
store/src/tasks.rs — status-transition helpers used by claim_for_dispatch; the same helpers would be used to roll back on failure.
QA plan
- Open
workflows/src/dispatch.rs and locate dispatch_inner — expect to see a rollback path (e.g. set status back to ready or failed) on any Err returned after claim_for_dispatch succeeds.
- Simulate a workspace creation failure by making
ensure_task_worktree return Err(anyhow!("test")), then run a dispatch — expect the task's status in SQLite to be ready or failed, not in-progress.
- Confirm no task row has
status = 'in-progress' without a live agent session after the simulated failure.
Done when
Any failure in dispatch_inner after claim_for_dispatch succeeds either rolls back the task status to ready or records failed, leaving no task permanently stuck in in-progress with no agent attached.
Current state
When
dispatch_innerfails to create a workspace or spawn a tmux window afterclaim_for_dispatchhas already set the task toin-progress, the task is left permanently stuck inin-progresswith no running agent attached and no automated recovery path. A developer must manually intervene in SQLite to reset the task status, or the task is silently abandoned — a ghost entry that occupies the in-progress slot indefinitely.Ideal state
ensure_task_worktreeor the tmux spawn afterclaim_for_dispatchsucceeds rolls the task status back toready(or records afailedstatus with an error message).in-progresswith no running agent attached.Out of scope
claim_for_dispatchsemantics or the happy path.Starting points
workflows/src/dispatch.rs—dispatch_inner:claim_for_dispatchis called around line 445;ensure_task_worktreeand the tmux spawn follow with no compensating rollback on failure.store/src/tasks.rs— status-transition helpers used byclaim_for_dispatch; the same helpers would be used to roll back on failure.QA plan
workflows/src/dispatch.rsand locatedispatch_inner— expect to see a rollback path (e.g. set status back toreadyorfailed) on anyErrreturned afterclaim_for_dispatchsucceeds.ensure_task_worktreereturnErr(anyhow!("test")), then run a dispatch — expect the task's status in SQLite to bereadyorfailed, notin-progress.status = 'in-progress'without a live agent session after the simulated failure.Done when
Any failure in
dispatch_innerafterclaim_for_dispatchsucceeds either rolls back the task status toreadyor recordsfailed, leaving no task permanently stuck inin-progresswith no agent attached.