summaryrefslogtreecommitdiff
path: root/internal/storage
diff options
context:
space:
mode:
Diffstat (limited to 'internal/storage')
-rw-r--r--internal/storage/db.go129
-rw-r--r--internal/storage/db_test.go183
2 files changed, 8 insertions, 304 deletions
diff --git a/internal/storage/db.go b/internal/storage/db.go
index e03a902..22a3d7b 100644
--- a/internal/storage/db.go
+++ b/internal/storage/db.go
@@ -123,21 +123,6 @@ func (s *DB) migrate() error {
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
)`,
- `CREATE TABLE IF NOT EXISTS stories (
- id TEXT PRIMARY KEY,
- name TEXT NOT NULL,
- project_id TEXT NOT NULL DEFAULT '',
- branch_name TEXT NOT NULL DEFAULT '',
- deploy_config TEXT NOT NULL DEFAULT '',
- validation_json TEXT NOT NULL DEFAULT '',
- status TEXT NOT NULL DEFAULT 'PENDING',
- created_at DATETIME NOT NULL,
- updated_at DATETIME NOT NULL
- )`,
- `ALTER TABLE tasks ADD COLUMN story_id TEXT`,
- `ALTER TABLE tasks ADD COLUMN acceptance_criteria TEXT NOT NULL DEFAULT ''`,
- `ALTER TABLE tasks ADD COLUMN checker_for_task_id TEXT NOT NULL DEFAULT ''`,
- `ALTER TABLE tasks ADD COLUMN checker_report TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE executions ADD COLUMN tokens_in INTEGER`,
`ALTER TABLE executions ADD COLUMN tokens_out INTEGER`,
`ALTER TABLE executions ADD COLUMN agent TEXT`,
@@ -198,12 +183,11 @@ func (s *DB) CreateTask(t *task.Task) error {
defer tx.Rollback() //nolint:errcheck
if _, err = tx.Exec(`
- INSERT INTO tasks (id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, story_id, acceptance_criteria, checker_for_task_id, checker_report)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
+ INSERT INTO tasks (id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
t.ID, t.Name, t.Description, t.ElaborationInput, t.Project, t.RepositoryURL, string(configJSON), string(t.Priority),
t.Timeout.Duration.Nanoseconds(), string(retryJSON), string(tagsJSON), string(depsJSON),
- t.ParentTaskID, string(t.State), t.CreatedAt.UTC(), t.UpdatedAt.UTC(), t.StoryID,
- t.AcceptanceCriteria, t.CheckerForTaskID, t.CheckerReport,
+ t.ParentTaskID, string(t.State), t.CreatedAt.UTC(), t.UpdatedAt.UTC(),
); err != nil {
return err
}
@@ -230,13 +214,13 @@ func (s *DB) CreateTask(t *task.Task) error {
// GetTask retrieves a task by ID.
func (s *DB) GetTask(id string) (*task.Task, error) {
- row := s.db.QueryRow(`SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json, story_id, acceptance_criteria, checker_for_task_id, checker_report FROM tasks WHERE id = ?`, id)
+ row := s.db.QueryRow(`SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json FROM tasks WHERE id = ?`, id)
return scanTask(row)
}
// ListTasks returns tasks matching the given filter.
func (s *DB) ListTasks(filter TaskFilter) ([]*task.Task, error) {
- query := `SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json, story_id, acceptance_criteria, checker_for_task_id, checker_report FROM tasks WHERE 1=1`
+ query := `SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json FROM tasks WHERE 1=1`
var args []interface{}
if filter.State != "" {
@@ -272,7 +256,7 @@ func (s *DB) ListTasks(filter TaskFilter) ([]*task.Task, error) {
// ListSubtasks returns all tasks whose parent_task_id matches the given ID.
func (s *DB) ListSubtasks(parentID string) ([]*task.Task, error) {
- rows, err := s.db.Query(`SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json, story_id, acceptance_criteria, checker_for_task_id, checker_report FROM tasks WHERE parent_task_id = ? ORDER BY created_at ASC`, parentID)
+ rows, err := s.db.Query(`SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json FROM tasks WHERE parent_task_id = ? ORDER BY created_at ASC`, parentID)
if err != nil {
return nil, err
}
@@ -342,7 +326,7 @@ func (s *DB) ResetTaskForRetry(id string) (*task.Task, error) {
}
defer tx.Rollback() //nolint:errcheck
- t, err := scanTask(tx.QueryRow(`SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json, story_id, acceptance_criteria, checker_for_task_id, checker_report FROM tasks WHERE id = ?`, id))
+ t, err := scanTask(tx.QueryRow(`SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json FROM tasks WHERE id = ?`, id))
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("task %q not found", id)
@@ -987,24 +971,6 @@ func (s *DB) UpdateTaskSummary(taskID, summary string) error {
return tx.Commit()
}
-// UpdateTaskCheckerReport sets the checker_report field on a task.
-func (s *DB) UpdateTaskCheckerReport(id, report string) error {
- now := time.Now().UTC()
- _, err := s.db.Exec(`UPDATE tasks SET checker_report = ?, updated_at = ? WHERE id = ?`, report, now, id)
- return err
-}
-
-// GetCheckerTask returns the checker task for the given checked task ID,
-// or nil if no checker task exists.
-func (s *DB) GetCheckerTask(checkedTaskID string) (*task.Task, error) {
- row := s.db.QueryRow(`SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json, story_id, acceptance_criteria, checker_for_task_id, checker_report FROM tasks WHERE checker_for_task_id = ? LIMIT 1`, checkedTaskID)
- t, err := scanTask(row)
- if err == sql.ErrNoRows {
- return nil, nil
- }
- return t, err
-}
-
// AppendTaskInteraction appends a Q&A interaction to the task's interaction history.
func (s *DB) AppendTaskInteraction(taskID string, interaction task.Interaction) error {
tx, err := s.db.Begin()
@@ -1102,17 +1068,12 @@ func scanTask(row scanner) (*task.Task, error) {
questionJSON sql.NullString
summary sql.NullString
interactionsJSON sql.NullString
- storyID sql.NullString
- acceptanceCriteria sql.NullString
- checkerForTaskID sql.NullString
- checkerReport sql.NullString
)
err := row.Scan(
&t.ID, &t.Name, &t.Description, &elaborationInput, &project, &repositoryURL,
&configJSON, &priority, &timeoutNS, &retryJSON, &tagsJSON, &depsJSON,
&parentTaskID, &state, &t.CreatedAt, &t.UpdatedAt,
- &rejectionComment, &questionJSON, &summary, &interactionsJSON, &storyID,
- &acceptanceCriteria, &checkerForTaskID, &checkerReport,
+ &rejectionComment, &questionJSON, &summary, &interactionsJSON,
)
t.ParentTaskID = parentTaskID.String
t.ElaborationInput = elaborationInput.String
@@ -1121,10 +1082,6 @@ func scanTask(row scanner) (*task.Task, error) {
t.RejectionComment = rejectionComment.String
t.QuestionJSON = questionJSON.String
t.Summary = summary.String
- t.StoryID = storyID.String
- t.AcceptanceCriteria = acceptanceCriteria.String
- t.CheckerForTaskID = checkerForTaskID.String
- t.CheckerReport = checkerReport.String
if err != nil {
return nil, err
}
@@ -1396,73 +1353,3 @@ func (s *DB) UpsertProject(p *task.Project) error {
return err
}
-// CreateStory inserts a new story.
-func (s *DB) CreateStory(st *task.Story) error {
- now := time.Now().UTC()
- _, err := s.db.Exec(
- `INSERT INTO stories (id, name, project_id, branch_name, deploy_config, validation_json, status, created_at, updated_at)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
- st.ID, st.Name, st.ProjectID, st.BranchName, st.DeployConfig, st.ValidationJSON, string(st.Status), now, now,
- )
- return err
-}
-
-// GetStory retrieves a story by ID.
-func (s *DB) GetStory(id string) (*task.Story, error) {
- row := s.db.QueryRow(`SELECT id, name, project_id, branch_name, deploy_config, validation_json, status, created_at, updated_at FROM stories WHERE id = ?`, id)
- st := &task.Story{}
- var status string
- if err := row.Scan(&st.ID, &st.Name, &st.ProjectID, &st.BranchName, &st.DeployConfig, &st.ValidationJSON, &status, &st.CreatedAt, &st.UpdatedAt); err != nil {
- return nil, err
- }
- st.Status = task.StoryState(status)
- return st, nil
-}
-
-// ListStories returns all stories ordered by creation time descending.
-func (s *DB) ListStories() ([]*task.Story, error) {
- rows, err := s.db.Query(`SELECT id, name, project_id, branch_name, deploy_config, validation_json, status, created_at, updated_at FROM stories ORDER BY created_at DESC`)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- var stories []*task.Story
- for rows.Next() {
- st := &task.Story{}
- var status string
- if err := rows.Scan(&st.ID, &st.Name, &st.ProjectID, &st.BranchName, &st.DeployConfig, &st.ValidationJSON, &status, &st.CreatedAt, &st.UpdatedAt); err != nil {
- return nil, err
- }
- st.Status = task.StoryState(status)
- stories = append(stories, st)
- }
- return stories, rows.Err()
-}
-
-// UpdateStoryStatus updates the status of a story.
-func (s *DB) UpdateStoryStatus(id string, status task.StoryState) error {
- now := time.Now().UTC()
- _, err := s.db.Exec(`UPDATE stories SET status = ?, updated_at = ? WHERE id = ?`, string(status), now, id)
- return err
-}
-
-// ListTasksByStory returns all tasks associated with a story, ordered by creation time ascending.
-func (s *DB) ListTasksByStory(storyID string) ([]*task.Task, error) {
- rows, err := s.db.Query(
- `SELECT id, name, description, elaboration_input, project, repository_url, config_json, priority, timeout_ns, retry_json, tags_json, depends_on_json, parent_task_id, state, created_at, updated_at, rejection_comment, question_json, summary, interactions_json, story_id, acceptance_criteria, checker_for_task_id, checker_report FROM tasks WHERE story_id = ? ORDER BY created_at ASC`,
- storyID,
- )
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- var tasks []*task.Task
- for rows.Next() {
- t, err := scanTaskRows(rows)
- if err != nil {
- return nil, err
- }
- tasks = append(tasks, t)
- }
- return tasks, rows.Err()
-}
diff --git a/internal/storage/db_test.go b/internal/storage/db_test.go
index 4d744a4..09bbdfc 100644
--- a/internal/storage/db_test.go
+++ b/internal/storage/db_test.go
@@ -1256,187 +1256,4 @@ func TestUpdateProject(t *testing.T) {
}
}
-func TestCreateStory(t *testing.T) {
- db := testDB(t)
- st := &task.Story{
- ID: "story-1",
- Name: "My Story",
- Status: task.StoryPending,
- }
- if err := db.CreateStory(st); err != nil {
- t.Fatalf("CreateStory: %v", err)
- }
-}
-
-func TestGetStory(t *testing.T) {
- db := testDB(t)
- st := &task.Story{
- ID: "story-2",
- Name: "Get Story",
- ProjectID: "proj-1",
- Status: task.StoryPending,
- }
- if err := db.CreateStory(st); err != nil {
- t.Fatalf("CreateStory: %v", err)
- }
- got, err := db.GetStory("story-2")
- if err != nil {
- t.Fatalf("GetStory: %v", err)
- }
- if got.Name != "Get Story" {
- t.Errorf("Name: want 'Get Story', got %q", got.Name)
- }
- if got.ProjectID != "proj-1" {
- t.Errorf("ProjectID: want 'proj-1', got %q", got.ProjectID)
- }
- if got.Status != task.StoryPending {
- t.Errorf("Status: want PENDING, got %q", got.Status)
- }
-}
-
-func TestListStories(t *testing.T) {
- db := testDB(t)
- for _, name := range []string{"A", "B", "C"} {
- if err := db.CreateStory(&task.Story{ID: name, Name: name, Status: task.StoryPending}); err != nil {
- t.Fatalf("CreateStory %s: %v", name, err)
- }
- }
- stories, err := db.ListStories()
- if err != nil {
- t.Fatalf("ListStories: %v", err)
- }
- if len(stories) != 3 {
- t.Errorf("want 3 stories, got %d", len(stories))
- }
-}
-
-func TestUpdateStoryStatus(t *testing.T) {
- db := testDB(t)
- st := &task.Story{ID: "story-upd", Name: "Upd", Status: task.StoryPending}
- if err := db.CreateStory(st); err != nil {
- t.Fatalf("CreateStory: %v", err)
- }
- if err := db.UpdateStoryStatus("story-upd", task.StoryInProgress); err != nil {
- t.Fatalf("UpdateStoryStatus: %v", err)
- }
- got, _ := db.GetStory("story-upd")
- if got.Status != task.StoryInProgress {
- t.Errorf("Status: want IN_PROGRESS, got %q", got.Status)
- }
-}
-
-func TestListTasksByStory(t *testing.T) {
- db := testDB(t)
- now := time.Now().UTC()
-
- if err := db.CreateStory(&task.Story{ID: "story-tasks", Name: "S", Status: task.StoryPending}); err != nil {
- t.Fatalf("CreateStory: %v", err)
- }
-
- makeTask := func(id string) *task.Task {
- return &task.Task{
- ID: id,
- Name: id,
- StoryID: "story-tasks",
- Agent: task.AgentConfig{Type: "claude"},
- Priority: task.PriorityNormal,
- Tags: []string{},
- DependsOn: []string{},
- Retry: task.RetryConfig{MaxAttempts: 1},
- State: task.StatePending,
- CreatedAt: now,
- UpdatedAt: now,
- }
- }
-
- if err := db.CreateTask(makeTask("t1")); err != nil {
- t.Fatal(err)
- }
- if err := db.CreateTask(makeTask("t2")); err != nil {
- t.Fatal(err)
- }
-
- tasks, err := db.ListTasksByStory("story-tasks")
- if err != nil {
- t.Fatalf("ListTasksByStory: %v", err)
- }
- if len(tasks) != 2 {
- t.Errorf("want 2 tasks, got %d", len(tasks))
- }
- for _, tk := range tasks {
- if tk.StoryID != "story-tasks" {
- t.Errorf("task %s: StoryID want 'story-tasks', got %q", tk.ID, tk.StoryID)
- }
- }
-}
-
-func TestUpdateTaskCheckerReport(t *testing.T) {
- db := testDB(t)
- tk := &task.Task{
- ID: "cr-1", Name: "orig", RepositoryURL: "https://github.com/x/y",
- 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: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
- }
- if err := db.CreateTask(tk); err != nil {
- t.Fatalf("CreateTask: %v", err)
- }
- if err := db.UpdateTaskCheckerReport("cr-1", "Tests failed: missing endpoint"); err != nil {
- t.Fatalf("UpdateTaskCheckerReport: %v", err)
- }
- got, err := db.GetTask("cr-1")
- if err != nil {
- t.Fatalf("GetTask: %v", err)
- }
- if got.CheckerReport != "Tests failed: missing endpoint" {
- t.Errorf("expected checker report, got %q", got.CheckerReport)
- }
-}
-
-func TestGetCheckerTask(t *testing.T) {
- db := testDB(t)
- checked := &task.Task{
- ID: "chk-orig", Name: "orig", RepositoryURL: "https://github.com/x/y",
- 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: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
- }
- if err := db.CreateTask(checked); err != nil {
- t.Fatalf("CreateTask checked: %v", err)
- }
- checker := &task.Task{
- ID: "chk-checker", Name: "Check: orig", CheckerForTaskID: "chk-orig",
- RepositoryURL: "https://github.com/x/y",
- Agent: task.AgentConfig{Type: "claude", Instructions: "validate"},
- Priority: task.PriorityNormal,
- Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "linear"},
- Tags: []string{}, DependsOn: []string{},
- State: task.StatePending, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
- }
- if err := db.CreateTask(checker); err != nil {
- t.Fatalf("CreateTask checker: %v", err)
- }
-
- // Should find the checker task.
- got, err := db.GetCheckerTask("chk-orig")
- if err != nil {
- t.Fatalf("GetCheckerTask: %v", err)
- }
- if got == nil || got.ID != "chk-checker" {
- t.Errorf("expected checker task ID chk-checker, got %v", got)
- }
-
- // Should return nil when no checker exists.
- none, err := db.GetCheckerTask("nonexistent")
- if err != nil {
- t.Fatalf("GetCheckerTask nonexistent: %v", err)
- }
- if none != nil {
- t.Errorf("expected nil for task with no checker, got %v", none)
- }
-}