summaryrefslogtreecommitdiff
path: root/internal/storage
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-03-09 07:11:57 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-03-10 09:29:02 +0000
commit0676f0f2e6d1ba371806ca4b808a4993027d86ea (patch)
treec8d6776d1b4f1b82e3ba2b392272f359fb0ca499 /internal/storage
parenta62a19bbf774289e2018e50454aa719c1a2a9070 (diff)
fix: ensure tasks are re-classified on manual restart
Updated handleRunTask to use ResetTaskForRetry, which clears the agent type and model. This ensures that manually restarted tasks are always re-classified, allowing the system to switch to a different agent if the previous one is rate-limited. Also improved Claude quota-exhaustion detection.
Diffstat (limited to 'internal/storage')
-rw-r--r--internal/storage/db.go38
1 files changed, 38 insertions, 0 deletions
diff --git a/internal/storage/db.go b/internal/storage/db.go
index 31d38ed..01ce902 100644
--- a/internal/storage/db.go
+++ b/internal/storage/db.go
@@ -210,6 +210,44 @@ func (s *DB) UpdateTaskState(id string, newState task.State) error {
return tx.Commit()
}
+// ResetTaskForRetry sets a task to QUEUED and clears its agent type/model
+// so it can be re-classified.
+func (s *DB) ResetTaskForRetry(id string) (*task.Task, error) {
+ tx, err := s.db.Begin()
+ if err != nil {
+ return nil, err
+ }
+ defer tx.Rollback() //nolint:errcheck
+
+ t, err := scanTask(tx.QueryRow(`SELECT id, name, description, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json FROM tasks WHERE id = ?`, id))
+ if err != nil {
+ if err == sql.ErrNoRows {
+ return nil, fmt.Errorf("task %q not found", id)
+ }
+ return nil, err
+ }
+
+ if !task.ValidTransition(t.State, task.StateQueued) {
+ return nil, fmt.Errorf("invalid state transition %s → %s for task %q", t.State, task.StateQueued, id)
+ }
+
+ t.Agent.Type = ""
+ t.Agent.Model = ""
+ configJSON, _ := json.Marshal(t.Agent)
+
+ now := time.Now().UTC()
+ if _, err := tx.Exec(`UPDATE tasks SET state = ?, config_json = ?, updated_at = ? WHERE id = ?`,
+ string(task.StateQueued), string(configJSON), now, id); err != nil {
+ return nil, err
+ }
+
+ if err := tx.Commit(); err != nil {
+ return nil, err
+ }
+ t.State = task.StateQueued
+ return t, nil
+}
+
// RejectTask sets a task's state to PENDING and stores the rejection comment.
func (s *DB) RejectTask(id, comment string) error {
now := time.Now().UTC()