Skip to content

Commit 2c06190

Browse files
danielkovclaude
andauthored
fix: unblock_task returns to todo and clears stale claims (#25)
unblock_task checked started_at to decide status, always returning to in_progress for tasks that had been worked on. It also preserved stale claim fields, which suppressed the task.next trigger's claim guard. Now always returns to todo and clears claim_owner/lease so the task.next event fires and workers can pick up the task. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c2b5299 commit 2c06190

File tree

2 files changed

+15
-6
lines changed

2 files changed

+15
-6
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
### Fix: unblock returns tasks to \`todo\` and clears stale claims
2+
3+
Previously, \`unblock_task\` checked \`started_at\` to decide whether to return a task to \`in_progress\` or \`todo\`. Since agents set \`started_at\` when they begin work, unblocking always returned tasks to \`in_progress\` — even when the block was due to external factors (e.g., missing files from concurrent projects) and the task needed to re-enter the scheduling queue.
4+
5+
Additionally, unblock didn't clear \`claim_owner\` or \`claim_lease_expires_at\`. This meant the \`task.next\` SQLite trigger's claim guard would suppress the event even after manually cycling the task through \`draft → todo\`. The net effect was that unblocked tasks became invisible to the worker daemon — no \`task.next\` event fired, so no worker would pick them up.
6+
7+
**What changed:**
8+
- \`unblock_task\` now always transitions to \`todo\`, regardless of \`started_at\`
9+
- Stale claim fields (\`claim_owner\`, \`claim_claimed_at\`, \`claim_lease_expires_at\`) are cleared on unblock
10+
- The \`task.next\` trigger now fires correctly when tasks are unblocked

src/services/task_service.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,12 @@ pub async fn block_task(pool: &SqlitePool, id: &str, reason: &str) -> Result<Tas
271271
pub async fn unblock_task(pool: &SqlitePool, id: &str) -> Result<Task> {
272272
let mut task = get_task(pool, id).await?;
273273

274-
// Return to in_progress if it was started, otherwise todo
275-
task.status = if task.started_at.is_some() {
276-
TaskStatus::InProgress.as_str().to_string()
277-
} else {
278-
TaskStatus::Todo.as_str().to_string()
279-
};
274+
task.status = TaskStatus::Todo.as_str().to_string();
280275
task.blocked_reason = None;
276+
// Clear stale claim so task.next trigger can fire
277+
task.claim_owner = None;
278+
task.claim_claimed_at = None;
279+
task.claim_lease_expires_at = None;
281280
// Set actor for trigger-based events (system action)
282281
task.last_edited_by = None;
283282

0 commit comments

Comments
 (0)