Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
83 lines
2.7 KiB
Go
83 lines
2.7 KiB
Go
package workspace
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
|
|
workspaceissues "github.com/tutti-os/tutti/packages/workspace/issues"
|
|
)
|
|
|
|
// CreateIssueWithTasks commits the Plan-derived Issue, its initial task graph,
|
|
// and topic activity together. Event publication happens above this store
|
|
// boundary only after this transaction returns successfully.
|
|
func (s *SQLiteStore) CreateIssueWithTasks(
|
|
ctx context.Context,
|
|
issue workspaceissues.Issue,
|
|
tasks []workspaceissues.Task,
|
|
) (workspaceissues.Issue, []workspaceissues.Task, error) {
|
|
if err := s.ensureIssueDatabase(); err != nil {
|
|
return workspaceissues.Issue{}, nil, err
|
|
}
|
|
if err := s.ensureIssueWorkspace(ctx, issue.WorkspaceID); err != nil {
|
|
return workspaceissues.Issue{}, nil, err
|
|
}
|
|
if len(tasks) == 0 {
|
|
return workspaceissues.Issue{}, nil, workspaceissues.ErrInvalidArgument
|
|
}
|
|
tx, err := s.writeDB.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return workspaceissues.Issue{}, nil, fmt.Errorf("begin create workspace issue with tasks: %w", err)
|
|
}
|
|
defer func() {
|
|
_ = tx.Rollback()
|
|
}()
|
|
|
|
var topicExists int
|
|
err = tx.QueryRowContext(ctx, `
|
|
SELECT 1
|
|
FROM workspace_issue_topics
|
|
WHERE workspace_id = ? AND topic_id = ?
|
|
`, issue.WorkspaceID, issue.TopicID).Scan(&topicExists)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return workspaceissues.Issue{}, nil, workspaceissues.ErrTopicNotFound
|
|
}
|
|
if err != nil {
|
|
return workspaceissues.Issue{}, nil, fmt.Errorf("get workspace issue topic for batch create: %w", err)
|
|
}
|
|
|
|
createdIssue, err := insertWorkspaceIssue(ctx, tx, issue)
|
|
if err != nil {
|
|
return workspaceissues.Issue{}, nil, err
|
|
}
|
|
createdTasks := make([]workspaceissues.Task, 0, len(tasks))
|
|
for index, task := range tasks {
|
|
if task.WorkspaceID != issue.WorkspaceID || task.IssueID != issue.IssueID {
|
|
return workspaceissues.Issue{}, nil, workspaceissues.ErrInvalidArgument
|
|
}
|
|
task.SortIndex = index + 1
|
|
createdTask, err := insertWorkspaceIssueTask(ctx, tx, task)
|
|
if err != nil {
|
|
return workspaceissues.Issue{}, nil, err
|
|
}
|
|
createdTasks = append(createdTasks, createdTask)
|
|
}
|
|
|
|
result, err := tx.ExecContext(ctx, `
|
|
UPDATE workspace_issue_topics
|
|
SET last_activity_at_unix_ms = ?
|
|
WHERE workspace_id = ? AND topic_id = ?
|
|
`, issue.UpdatedAtUnixMS, issue.WorkspaceID, issue.TopicID)
|
|
if err != nil {
|
|
return workspaceissues.Issue{}, nil, fmt.Errorf("touch workspace issue topic activity during batch create: %w", err)
|
|
}
|
|
if err := requireRowsAffected(result, workspaceissues.ErrTopicNotFound, "touch workspace issue topic activity during batch create"); err != nil {
|
|
return workspaceissues.Issue{}, nil, err
|
|
}
|
|
if err := tx.Commit(); err != nil {
|
|
return workspaceissues.Issue{}, nil, fmt.Errorf("commit create workspace issue with tasks: %w", err)
|
|
}
|
|
return createdIssue, createdTasks, nil
|
|
}
|