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
78 lines
2.8 KiB
Go
78 lines
2.8 KiB
Go
package workspace
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
|
|
workspaceissues "github.com/tutti-os/tutti/packages/workspace/issues"
|
|
)
|
|
|
|
// CreateIssueWithContextRefs atomically commits an Issue, its managed-file
|
|
// references, and topic activity. Attachment bytes are staged before this
|
|
// transaction and startup reconciliation removes only files that never gained
|
|
// a committed reference.
|
|
func (s *SQLiteStore) CreateIssueWithContextRefs(
|
|
ctx context.Context,
|
|
issue workspaceissues.Issue,
|
|
refs []workspaceissues.ContextRef,
|
|
) (workspaceissues.Issue, []workspaceissues.ContextRef, 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(refs) == 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 context refs: %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 attachment create: %w", err)
|
|
}
|
|
createdIssue, err := insertWorkspaceIssue(ctx, tx, issue)
|
|
if err != nil {
|
|
return workspaceissues.Issue{}, nil, err
|
|
}
|
|
savedRefs := make([]workspaceissues.ContextRef, 0, len(refs))
|
|
for _, ref := range refs {
|
|
if ref.WorkspaceID != issue.WorkspaceID || ref.IssueID != issue.IssueID ||
|
|
ref.TaskID != "" || ref.ParentKind != workspaceissues.ContextRefParentIssue {
|
|
return workspaceissues.Issue{}, nil, workspaceissues.ErrInvalidArgument
|
|
}
|
|
created, err := insertWorkspaceIssueContextRef(ctx, tx, ref)
|
|
if err != nil {
|
|
return workspaceissues.Issue{}, nil, err
|
|
}
|
|
savedRefs = append(savedRefs, created)
|
|
}
|
|
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 attachment create: %w", err)
|
|
}
|
|
if err := requireRowsAffected(result, workspaceissues.ErrTopicNotFound, "touch workspace issue topic activity during attachment 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 context refs: %w", err)
|
|
}
|
|
return createdIssue, savedRefs, nil
|
|
}
|