diff options
Diffstat (limited to 'internal/storage')
| -rw-r--r-- | internal/storage/db.go | 17 | ||||
| -rw-r--r-- | internal/storage/db_test.go | 68 | ||||
| -rw-r--r-- | internal/storage/templates.go | 8 |
3 files changed, 68 insertions, 25 deletions
diff --git a/internal/storage/db.go b/internal/storage/db.go index cbbd97c..0a4f7a5 100644 --- a/internal/storage/db.go +++ b/internal/storage/db.go @@ -109,7 +109,7 @@ func isColumnExistsError(err error) bool { // CreateTask inserts a task into the database. func (s *DB) CreateTask(t *task.Task) error { - configJSON, err := json.Marshal(t.Claude) + configJSON, err := json.Marshal(t.Agent) if err != nil { return fmt.Errorf("marshaling config: %w", err) } @@ -242,7 +242,7 @@ func (s *DB) RejectTask(id, comment string) error { type TaskUpdate struct { Name string Description string - Config task.ClaudeConfig + Config task.AgentConfig Priority task.Priority TimeoutNS int64 Retry task.RetryConfig @@ -522,8 +522,17 @@ func scanTask(row scanner) (*task.Task, error) { t.State = task.State(state) t.Priority = task.Priority(priority) t.Timeout.Duration = time.Duration(timeoutNS) - if err := json.Unmarshal([]byte(configJSON), &t.Claude); err != nil { - return nil, fmt.Errorf("unmarshaling config: %w", err) + if err := json.Unmarshal([]byte(configJSON), &t.Agent); err != nil { + return nil, fmt.Errorf("unmarshaling agent config: %w", err) + } + // Fallback for legacy 'claude' field + if t.Agent.Instructions == "" { + var legacy struct { + Claude task.AgentConfig `json:"claude"` + } + if err := json.Unmarshal([]byte(configJSON), &legacy); err == nil && legacy.Claude.Instructions != "" { + t.Agent = legacy.Claude + } } if err := json.Unmarshal([]byte(retryJSON), &t.Retry); err != nil { return nil, fmt.Errorf("unmarshaling retry: %w", err) diff --git a/internal/storage/db_test.go b/internal/storage/db_test.go index 2738a41..f737096 100644 --- a/internal/storage/db_test.go +++ b/internal/storage/db_test.go @@ -37,7 +37,8 @@ func TestCreateTask_AndGetTask(t *testing.T) { ID: "task-1", Name: "Test Task", Description: "A test", - Claude: task.ClaudeConfig{ + Agent: task.AgentConfig{ + Type: "claude", Model: "sonnet", Instructions: "do it", ProjectDir: "/tmp", @@ -64,11 +65,11 @@ func TestCreateTask_AndGetTask(t *testing.T) { if got.Name != "Test Task" { t.Errorf("name: want 'Test Task', got %q", got.Name) } - if got.Claude.Model != "sonnet" { - t.Errorf("model: want 'sonnet', got %q", got.Claude.Model) + if got.Agent.Model != "sonnet" { + t.Errorf("model: want 'sonnet', got %q", got.Agent.Model) } - if got.Claude.MaxBudgetUSD != 2.5 { - t.Errorf("budget: want 2.5, got %f", got.Claude.MaxBudgetUSD) + if got.Agent.MaxBudgetUSD != 2.5 { + t.Errorf("budget: want 2.5, got %f", got.Agent.MaxBudgetUSD) } if got.Priority != task.PriorityHigh { t.Errorf("priority: want 'high', got %q", got.Priority) @@ -93,7 +94,7 @@ func TestUpdateTaskState(t *testing.T) { tk := &task.Task{ ID: "task-2", Name: "Stateful", - Claude: task.ClaudeConfig{Instructions: "test"}, + Agent: task.AgentConfig{Type: "claude", Instructions: "test"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{}, @@ -162,7 +163,7 @@ func TestListTasks_FilterByState(t *testing.T) { for i, state := range []task.State{task.StatePending, task.StatePending, task.StateRunning} { tk := &task.Task{ ID: fmt.Sprintf("t-%d", i), Name: fmt.Sprintf("Task %d", i), - Claude: task.ClaudeConfig{Instructions: "x"}, Priority: task.PriorityNormal, + Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{}, DependsOn: []string{}, State: state, CreatedAt: now, UpdatedAt: now, @@ -195,7 +196,7 @@ func TestListTasks_WithLimit(t *testing.T) { for i := 0; i < 5; i++ { tk := &task.Task{ ID: fmt.Sprintf("lt-%d", i), Name: fmt.Sprintf("T%d", i), - Claude: task.ClaudeConfig{Instructions: "x"}, Priority: task.PriorityNormal, + Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{}, DependsOn: []string{}, State: task.StatePending, CreatedAt: now.Add(time.Duration(i) * time.Second), UpdatedAt: now, @@ -218,7 +219,7 @@ func TestCreateExecution_AndGet(t *testing.T) { // Need a task first. tk := &task.Task{ - ID: "etask", Name: "E", Claude: task.ClaudeConfig{Instructions: "x"}, + ID: "etask", Name: "E", Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{}, DependsOn: []string{}, State: task.StatePending, CreatedAt: now, UpdatedAt: now, @@ -259,7 +260,7 @@ func TestListExecutions(t *testing.T) { db := testDB(t) now := time.Now().UTC() tk := &task.Task{ - ID: "ltask", Name: "L", Claude: task.ClaudeConfig{Instructions: "x"}, + ID: "ltask", Name: "L", Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{}, DependsOn: []string{}, State: task.StatePending, CreatedAt: now, UpdatedAt: now, @@ -292,7 +293,7 @@ func TestDB_UpdateTask(t *testing.T) { ID: "upd-1", Name: "Original Name", Description: "original desc", - Claude: task.ClaudeConfig{Model: "sonnet", Instructions: "original"}, + Agent: task.AgentConfig{Type: "claude", Model: "sonnet", Instructions: "original"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{"old"}, @@ -309,7 +310,7 @@ func TestDB_UpdateTask(t *testing.T) { u := TaskUpdate{ Name: "Updated Name", Description: "updated desc", - Config: task.ClaudeConfig{Model: "opus", Instructions: "updated"}, + Config: task.AgentConfig{Type: "claude", Model: "opus", Instructions: "updated"}, Priority: task.PriorityHigh, TimeoutNS: int64(15 * time.Minute), Retry: task.RetryConfig{MaxAttempts: 3, Backoff: "exponential"}, @@ -330,8 +331,8 @@ func TestDB_UpdateTask(t *testing.T) { if got.Description != "updated desc" { t.Errorf("description: want 'updated desc', got %q", got.Description) } - if got.Claude.Model != "opus" { - t.Errorf("model: want 'opus', got %q", got.Claude.Model) + if got.Agent.Model != "opus" { + t.Errorf("model: want 'opus', got %q", got.Agent.Model) } if got.Priority != task.PriorityHigh { t.Errorf("priority: want 'high', got %q", got.Priority) @@ -376,7 +377,7 @@ func TestRejectTask(t *testing.T) { db := testDB(t) now := time.Now().UTC() tk := &task.Task{ - ID: "reject-1", Name: "R", Claude: task.ClaudeConfig{Instructions: "x"}, + ID: "reject-1", Name: "R", Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{}, DependsOn: []string{}, State: task.StateReady, CreatedAt: now, UpdatedAt: now, @@ -413,7 +414,7 @@ func TestUpdateExecution(t *testing.T) { db := testDB(t) now := time.Now().UTC() tk := &task.Task{ - ID: "utask", Name: "U", Claude: task.ClaudeConfig{Instructions: "x"}, + ID: "utask", Name: "U", Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{}, DependsOn: []string{}, State: task.StatePending, CreatedAt: now, UpdatedAt: now, @@ -456,7 +457,7 @@ func TestUpdateExecution(t *testing.T) { func makeTestTask(id string, now time.Time) *task.Task { return &task.Task{ - ID: id, Name: "T-" + id, Claude: task.ClaudeConfig{Instructions: "x"}, + ID: id, Name: "T-" + id, Agent: task.AgentConfig{Type: "claude", Instructions: "x"}, Priority: task.PriorityNormal, Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"}, Tags: []string{}, DependsOn: []string{}, State: task.StatePending, CreatedAt: now, UpdatedAt: now, @@ -579,3 +580,36 @@ func TestStorage_GetLatestExecution(t *testing.T) { t.Errorf("want le-2, got %q", got.ID) } } + +func TestGetTask_BackwardCompatibility(t *testing.T) { + db := testDB(t) + now := time.Now().UTC().Truncate(time.Second) + + // Legacy config JSON using "claude" field instead of "agent" + legacyConfig := `{"claude":{"model":"haiku","instructions":"legacy instructions","max_budget_usd":0.5}}` + + _, err := db.db.Exec(` + INSERT INTO tasks (id, name, description, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, state, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + "legacy-id", "Legacy Task", "A legacy test", legacyConfig, "normal", + 0, "{}", "[]", "[]", "PENDING", now, now, + ) + if err != nil { + t.Fatalf("inserting legacy task: %v", err) + } + + got, err := db.GetTask("legacy-id") + if err != nil { + t.Fatalf("getting legacy task: %v", err) + } + + if got.Agent.Instructions != "legacy instructions" { + t.Errorf("instructions: want 'legacy instructions', got %q", got.Agent.Instructions) + } + if got.Agent.Model != "haiku" { + t.Errorf("model: want 'haiku', got %q", got.Agent.Model) + } + if got.Agent.MaxBudgetUSD != 0.5 { + t.Errorf("budget: want 0.5, got %f", got.Agent.MaxBudgetUSD) + } +} diff --git a/internal/storage/templates.go b/internal/storage/templates.go index 350b4f8..57abaa4 100644 --- a/internal/storage/templates.go +++ b/internal/storage/templates.go @@ -18,7 +18,7 @@ type Template struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` - Claude task.ClaudeConfig `json:"claude"` + Agent task.AgentConfig `json:"agent"` Timeout string `json:"timeout"` Priority string `json:"priority"` Tags []string `json:"tags"` @@ -28,7 +28,7 @@ type Template struct { // CreateTemplate inserts a new template. func (s *DB) CreateTemplate(tmpl *Template) error { - configJSON, err := json.Marshal(tmpl.Claude) + configJSON, err := json.Marshal(tmpl.Agent) if err != nil { return fmt.Errorf("marshaling config: %w", err) } @@ -73,7 +73,7 @@ func (s *DB) ListTemplates() ([]*Template, error) { // UpdateTemplate fully replaces a template's fields. Returns ErrTemplateNotFound if the ID is missing. func (s *DB) UpdateTemplate(tmpl *Template) error { - configJSON, err := json.Marshal(tmpl.Claude) + configJSON, err := json.Marshal(tmpl.Agent) if err != nil { return fmt.Errorf("marshaling config: %w", err) } @@ -130,7 +130,7 @@ func scanTemplate(row scanner) (*Template, error) { } return nil, err } - if err := json.Unmarshal([]byte(configJSON), &tmpl.Claude); err != nil { + if err := json.Unmarshal([]byte(configJSON), &tmpl.Agent); err != nil { return nil, fmt.Errorf("unmarshaling config: %w", err) } if err := json.Unmarshal([]byte(tagsJSON), &tmpl.Tags); err != nil { |
