summaryrefslogtreecommitdiff
path: root/internal/storage/db.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/storage/db.go')
-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()