summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-03-11 08:25:23 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-03-11 08:25:23 +0000
commit889791e9a2d9585a35e72402bfc9e6feb49fbb88 (patch)
tree1f9c4ecdb71906e81c4511ca1b3c1ae428581a44
parent1b5e7177769c79f9e836a55f9c008a295e2ff975 (diff)
fix: clear question_json when restarting a task via ResetTaskForRetry
A BLOCKED task that fails on resume would keep its stale question_json after being restarted. The frontend then showed "waiting for your input" with the old prompt even though the task was running fresh. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--internal/storage/db.go2
-rw-r--r--internal/storage/db_test.go35
2 files changed, 36 insertions, 1 deletions
diff --git a/internal/storage/db.go b/internal/storage/db.go
index 5ba0786..aaf1e09 100644
--- a/internal/storage/db.go
+++ b/internal/storage/db.go
@@ -237,7 +237,7 @@ func (s *DB) ResetTaskForRetry(id string) (*task.Task, error) {
configJSON, _ := json.Marshal(t.Agent)
now := time.Now().UTC()
- if _, err := tx.Exec(`UPDATE tasks SET state = ?, config_json = ?, updated_at = ? WHERE id = ?`,
+ if _, err := tx.Exec(`UPDATE tasks SET state = ?, config_json = ?, question_json = NULL, updated_at = ? WHERE id = ?`,
string(task.StateQueued), string(configJSON), now, id); err != nil {
return nil, err
}
diff --git a/internal/storage/db_test.go b/internal/storage/db_test.go
index d28a4a8..2956be0 100644
--- a/internal/storage/db_test.go
+++ b/internal/storage/db_test.go
@@ -628,6 +628,41 @@ func TestDeleteTask_DeepSubtaskCascadeAtomic(t *testing.T) {
}
}
+// TestResetTaskForRetry_ClearsQuestionJSON verifies that restarting a BLOCKED
+// or FAILED task via ResetTaskForRetry clears any stale question so the frontend
+// does not show a stale "waiting for input" prompt.
+func TestResetTaskForRetry_ClearsQuestionJSON(t *testing.T) {
+ db := testDB(t)
+ now := time.Now().UTC()
+ tk := makeTestTask("retry-task", now)
+ tk.State = task.StatePending
+ db.CreateTask(tk)
+
+ // Transition to BLOCKED with a question.
+ db.UpdateTaskState("retry-task", task.StateQueued)
+ db.UpdateTaskState("retry-task", task.StateRunning)
+ db.UpdateTaskState("retry-task", task.StateBlocked)
+ db.UpdateTaskQuestion("retry-task", `{"question":"which branch?"}`)
+
+ // Simulate the task failing and being restarted.
+ db.UpdateTaskState("retry-task", task.StateFailed)
+
+ if _, err := db.ResetTaskForRetry("retry-task"); err != nil {
+ t.Fatalf("ResetTaskForRetry: %v", err)
+ }
+
+ got, err := db.GetTask("retry-task")
+ if err != nil {
+ t.Fatalf("GetTask: %v", err)
+ }
+ if got.QuestionJSON != "" {
+ t.Errorf("question_json should be cleared after reset, got %q", got.QuestionJSON)
+ }
+ if got.State != task.StateQueued {
+ t.Errorf("state should be QUEUED, got %q", got.State)
+ }
+}
+
func TestStorage_GetLatestExecution(t *testing.T) {
db := testDB(t)
now := time.Now().UTC()