summaryrefslogtreecommitdiff
path: root/internal/storage
diff options
context:
space:
mode:
Diffstat (limited to 'internal/storage')
-rw-r--r--internal/storage/db.go17
-rw-r--r--internal/storage/db_test.go68
-rw-r--r--internal/storage/templates.go8
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 {