package sqlite import ( "context" "database/sql" "fmt" "inbox/internal/domain/taskgraph" ) func (s *Store) CreateTaskGraphVersion(ctx context.Context, value taskgraph.Record) (taskgraph.Record, error) { if err := value.Validate(); err != nil { return taskgraph.Record{}, err } if value.ID == "" { id, err := s.newID("task-graph-version") if err != nil { return taskgraph.Record{}, err } value.ID = id } value.CreatedAt = coalesceString(value.CreatedAt, s.now()) if _, err := s.db.ExecContext(ctx, ` INSERT INTO task_graph_versions( id, topic_id, version, status, plan_json, plan_summary_markdown, created_by_role_name, created_at, confirmed_at, supersedes_graph_version_id ) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, value.ID, value.TopicID, value.Version, string(value.Status), value.PlanJSON, value.PlanSummaryMarkdown, value.CreatedByRoleName, value.CreatedAt, nullableString(value.ConfirmedAt), nullableString(value.SupersedesGraphVersionID), ); err != nil { return taskgraph.Record{}, fmt.Errorf("create task graph version: %w", err) } return value, nil } func (s *Store) GetTaskGraphVersion(ctx context.Context, graphVersionID string) (taskgraph.Record, error) { row := s.db.QueryRowContext(ctx, ` SELECT id, topic_id, version, status, plan_json, plan_summary_markdown, created_by_role_name, created_at, confirmed_at, supersedes_graph_version_id FROM task_graph_versions WHERE id = ? `, graphVersionID) return scanTaskGraphVersion(row) } func (s *Store) ListTaskGraphVersionsByTopic(ctx context.Context, topicID string) ([]taskgraph.Record, error) { rows, err := s.db.QueryContext(ctx, ` SELECT id, topic_id, version, status, plan_json, plan_summary_markdown, created_by_role_name, created_at, confirmed_at, supersedes_graph_version_id FROM task_graph_versions WHERE topic_id = ? ORDER BY version DESC, created_at DESC `, topicID) if err != nil { return nil, fmt.Errorf("list task graph versions by topic: %w", err) } defer rows.Close() var out []taskgraph.Record for rows.Next() { item, err := scanTaskGraphVersion(rows) if err != nil { return nil, err } out = append(out, item) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate task graph versions: %w", err) } return out, nil } func (s *Store) GetLatestTaskGraphVersionByTopic(ctx context.Context, topicID string) (taskgraph.Record, error) { row := s.db.QueryRowContext(ctx, ` SELECT id, topic_id, version, status, plan_json, plan_summary_markdown, created_by_role_name, created_at, confirmed_at, supersedes_graph_version_id FROM task_graph_versions WHERE topic_id = ? ORDER BY version DESC, created_at DESC LIMIT 1 `, topicID) return scanTaskGraphVersion(row) } func (s *Store) UpdateTaskGraphVersion(ctx context.Context, value taskgraph.Record) (taskgraph.Record, error) { if value.ID == "" { return taskgraph.Record{}, fmt.Errorf("task graph version id is required") } before, err := s.GetTaskGraphVersion(ctx, value.ID) if err != nil { return taskgraph.Record{}, err } value.TopicID = before.TopicID value.Version = before.Version value.CreatedAt = before.CreatedAt if err := value.Validate(); err != nil { return taskgraph.Record{}, err } if _, err := s.db.ExecContext(ctx, ` UPDATE task_graph_versions SET status = ?, plan_json = ?, plan_summary_markdown = ?, created_by_role_name = ?, confirmed_at = ?, supersedes_graph_version_id = ? WHERE id = ? `, string(value.Status), value.PlanJSON, value.PlanSummaryMarkdown, value.CreatedByRoleName, nullableString(value.ConfirmedAt), nullableString(value.SupersedesGraphVersionID), value.ID, ); err != nil { return taskgraph.Record{}, fmt.Errorf("update task graph version: %w", err) } return s.GetTaskGraphVersion(ctx, value.ID) } func scanTaskGraphVersion(s scanner) (taskgraph.Record, error) { var item taskgraph.Record var status string var confirmedAt sql.NullString var supersedesID sql.NullString if err := s.Scan( &item.ID, &item.TopicID, &item.Version, &status, &item.PlanJSON, &item.PlanSummaryMarkdown, &item.CreatedByRoleName, &item.CreatedAt, &confirmedAt, &supersedesID, ); err != nil { return taskgraph.Record{}, err } item.Status = taskgraph.Status(status) if confirmedAt.Valid { item.ConfirmedAt = confirmedAt.String } if supersedesID.Valid { item.SupersedesGraphVersionID = supersedesID.String } return item, nil }