task

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 2, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	EventTaskCreated  = "task_created"
	EventTaskUpdated  = "task_updated"
	EventTaskDeleted  = "task_deleted"
	EventLogsAppended = "logs_appended"
)

EventType identifies the kind of SSE event.

Variables

View Source
var ErrTaskNoPR = errtag.Tag[ErrTagTaskNoPR](
	errors.New("task has no pull request or branch"),
)

ErrTaskNoPR is returned when a move-to-review is attempted on a failed task that has no PR or branch.

View Source
var ErrTaskNotFailed = errtag.Tag[ErrTagTaskConflict](
	errors.New("task is not in failed status"),
)

ErrTaskNotFailed is returned when a move-to-review is attempted on a task that is not in failed status.

View Source
var ErrTaskNotPending = errtag.Tag[ErrTagTaskNotPending](
	errors.New("task is no longer pending"),
)

ErrTaskNotPending is returned when an update is attempted on a task that is no longer in pending status.

Functions

This section is empty.

Types

type ActiveAgent

type ActiveAgent struct {
	TaskID     string  `json:"task_id"`
	TaskTitle  string  `json:"task_title"`
	RepoID     string  `json:"repo_id"`
	StartedAt  string  `json:"started_at"`
	RunningFor int64   `json:"running_for_ms"`
	Attempt    int     `json:"attempt"`
	CostUSD    float64 `json:"cost_usd"`
	Model      string  `json:"model,omitempty"`
	EpicID     string  `json:"epic_id,omitempty"`
	IsPlanning bool    `json:"is_planning,omitempty"`
	EpicTitle  string  `json:"epic_title,omitempty"`
}

ActiveAgent describes a single running agent session.

type AgentMetrics

type AgentMetrics struct {
	// Currently running agents
	RunningAgents int `json:"running_agents"`
	// Tasks pending to be picked up
	PendingTasks int `json:"pending_tasks"`
	// Tasks in review (PR created, awaiting merge)
	ReviewTasks int `json:"review_tasks"`
	// Total tasks (all statuses)
	TotalTasks int `json:"total_tasks"`

	// Completed tasks (merged + closed)
	CompletedTasks int `json:"completed_tasks"`
	// Failed tasks
	FailedTasks int `json:"failed_tasks"`

	// Total cost across all tasks (USD)
	TotalCostUSD float64 `json:"total_cost_usd"`

	// Details about each currently running agent
	ActiveAgents []ActiveAgent `json:"active_agents"`

	// Recent completions (last 10 tasks that reached a terminal state)
	RecentCompletions []CompletedAgent `json:"recent_completions"`

	// Workers actively polling for tasks
	Workers []workertracker.WorkerInfo `json:"workers"`
}

AgentMetrics provides a snapshot of agent activity and performance.

type Broker

type Broker struct {
	// contains filtered or unexported fields
}

Broker fans out task events to SSE subscribers. When a Notifier is configured, events are routed through the external notification system so that multiple server instances can share events. When nil, events are fanned out directly in-process.

func NewBroker

func NewBroker(notifier Notifier) *Broker

NewBroker creates a new Broker. If notifier is non-nil, Publish sends events through the external notification system; otherwise events are fanned out locally.

func (*Broker) Publish

func (b *Broker) Publish(ctx context.Context, event Event)

Publish sends an event. If a notifier is configured, the event is serialized and sent through the external notification system (the LISTEN side calls Receive to fan out locally). Otherwise, fans out directly to local subscribers.

func (*Broker) Receive

func (b *Broker) Receive(event Event)

Receive is called by an external listener (e.g., PG LISTEN loop) to inject an event for local fan-out to SSE subscribers.

func (*Broker) Subscribe

func (b *Broker) Subscribe() chan Event

Subscribe returns a buffered channel that receives task events.

func (*Broker) Unsubscribe

func (b *Broker) Unsubscribe(ch chan Event)

Unsubscribe removes and closes a subscriber channel.

type CompletedAgent

type CompletedAgent struct {
	TaskID     string  `json:"task_id"`
	TaskTitle  string  `json:"task_title"`
	RepoID     string  `json:"repo_id"`
	Status     string  `json:"status"`
	DurationMs *int64  `json:"duration_ms,omitempty"`
	CostUSD    float64 `json:"cost_usd"`
	Attempt    int     `json:"attempt"`
	FinishedAt string  `json:"finished_at"`
}

CompletedAgent describes a recently completed agent session.

type ErrTagTaskConflict

type ErrTagTaskConflict struct{ errtag.Conflict }

ErrTagTaskConflict indicates a task conflict (e.g. duplicate ID).

func (ErrTagTaskConflict) Msg

func (ErrTagTaskConflict) Msg() string

func (ErrTagTaskConflict) Unwrap

func (e ErrTagTaskConflict) Unwrap() error

type ErrTagTaskNoPR

type ErrTagTaskNoPR struct{ errtag.InvalidArgument }

ErrTagTaskNoPR indicates a task has no PR or branch to move to review.

func (ErrTagTaskNoPR) Msg

func (ErrTagTaskNoPR) Msg() string

func (ErrTagTaskNoPR) Unwrap

func (e ErrTagTaskNoPR) Unwrap() error

type ErrTagTaskNotFound

type ErrTagTaskNotFound struct{ errtag.NotFound }

ErrTagTaskNotFound indicates a task was not found.

func (ErrTagTaskNotFound) Msg

func (ErrTagTaskNotFound) Msg() string

func (ErrTagTaskNotFound) Unwrap

func (e ErrTagTaskNotFound) Unwrap() error

type ErrTagTaskNotPending

type ErrTagTaskNotPending struct{ errtag.Conflict }

ErrTagTaskNotPending indicates an operation was rejected because the task is not in pending status.

func (ErrTagTaskNotPending) Msg

func (ErrTagTaskNotPending) Unwrap

func (e ErrTagTaskNotPending) Unwrap() error

type Event

type Event struct {
	Type    string   `json:"type"`
	RepoID  string   `json:"repo_id,omitempty"`
	Task    *Task    `json:"task,omitempty"`
	TaskID  TaskID   `json:"task_id,omitempty"`
	Logs    []string `json:"logs,omitempty"`
	Attempt int      `json:"attempt,omitempty"`
}

Event represents a task mutation broadcast to SSE subscribers.

type Notifier

type Notifier interface {
	Notify(ctx context.Context, payload []byte) error
}

Notifier sends event payloads to an external notification system (e.g., PostgreSQL NOTIFY). The listen side of the notification system calls Broker.Receive to complete the fan-out.

type PlanningEpic

type PlanningEpic struct {
	ID        string
	Title     string
	RepoID    string
	Model     string
	ClaimedAt *time.Time
}

PlanningEpic represents an epic that is actively being planned by an agent. This is a minimal struct to avoid importing the epic package (which would create a circular dependency).

type PlanningEpicLister

type PlanningEpicLister interface {
	ListPlanningEpicsForMetrics(ctx context.Context) ([]PlanningEpic, error)
}

PlanningEpicLister lists epics that are actively being planned.

type PlanningEpicListerFunc

type PlanningEpicListerFunc struct {
	// contains filtered or unexported fields
}

PlanningEpicListerFunc adapts a function to the PlanningEpicLister interface.

func NewPlanningEpicListerFunc

func NewPlanningEpicListerFunc(fn func(ctx context.Context) ([]PlanningEpic, error)) *PlanningEpicListerFunc

NewPlanningEpicListerFunc creates a PlanningEpicLister from a function.

func (*PlanningEpicListerFunc) ListPlanningEpicsForMetrics

func (f *PlanningEpicListerFunc) ListPlanningEpicsForMetrics(ctx context.Context) ([]PlanningEpic, error)

type Repository

type Repository interface {
	TaskRepository
	tx.Repository[Repository]
}

Repository is the interface for performing CRUD operations on Tasks.

Implementations must handle all database-specific concerns (error mapping, marshalling) and return domain types.

type StartOverTaskParams

type StartOverTaskParams struct {
	Title              string
	Description        string
	AcceptanceCriteria []string
}

StartOverTaskParams holds the fields that can be updated when starting a task over.

type Status

type Status string

Status represents the lifecycle state of a Task.

const (
	StatusPending Status = "pending"
	StatusRunning Status = "running"
	StatusReview  Status = "review" // PR created, awaiting review/merge
	StatusMerged  Status = "merged" // PR has been merged
	StatusClosed  Status = "closed" // Manually closed by user
	StatusFailed  Status = "failed"
)

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store wraps a Repository and adds application-level concerns such as pending task notification, dependency validation, and event broadcasting.

func NewStore

func NewStore(repo Repository, broker *Broker) *Store

NewStore creates a new Store backed by the given Repository and Broker.

func (*Store) AddCost

func (s *Store) AddCost(ctx context.Context, id TaskID, costUSD float64) error

AddCost adds to the accumulated cost for a task.

func (*Store) AppendTaskLogs

func (s *Store) AppendTaskLogs(ctx context.Context, id TaskID, attempt int, logs []string) error

AppendTaskLogs appends log lines to a task for the given attempt.

func (*Store) BulkCloseTasksByEpic

func (s *Store) BulkCloseTasksByEpic(ctx context.Context, epicID, reason string) error

BulkCloseTasksByEpic closes all non-terminal tasks for an epic and publishes update events for each affected task.

func (*Store) BulkDeleteTasksByEpic

func (s *Store) BulkDeleteTasksByEpic(ctx context.Context, epicID string) error

BulkDeleteTasksByEpic deletes all tasks (and their logs) belonging to an epic and publishes deletion events for each affected task.

func (*Store) BulkDeleteTasksByIDs

func (s *Store) BulkDeleteTasksByIDs(ctx context.Context, ids []string) error

BulkDeleteTasksByIDs deletes multiple tasks (and their logs) by ID and publishes deletion events for each affected task.

func (*Store) ClaimPendingTask

func (s *Store) ClaimPendingTask(ctx context.Context, repoIDs []string) (*Task, error)

ClaimPendingTask finds a pending task with all dependencies met and claims it by setting its status to running. When repoIDs is non-empty, only tasks belonging to those repos are considered. The read-check-claim flow is wrapped in a transaction and uses optimistic locking (WHERE status = 'pending') so that concurrent workers cannot claim the same task.

func (*Store) ClearEpicIDForTasks

func (s *Store) ClearEpicIDForTasks(ctx context.Context, epicID string) error

ClearEpicIDForTasks removes the epic_id foreign key from all tasks belonging to the given epic. This must be called before deleting an epic to avoid FK constraint violations.

func (*Store) CloseTask

func (s *Store) CloseTask(ctx context.Context, id TaskID, reason string) error

CloseTask closes a task with an optional reason.

func (*Store) CreateTask

func (s *Store) CreateTask(ctx context.Context, task *Task) error

CreateTask validates dependencies and creates a new task.

func (*Store) CreateTaskFromEpic

func (s *Store) CreateTaskFromEpic(ctx context.Context, repoID, title, description string, dependsOn, acceptanceCriteria []string, epicID string, ready bool, model string) (string, error)

CreateTaskFromEpic creates a task associated with an epic. Dependencies are not validated since they are created in the same batch.

func (*Store) DeleteExpiredLogs

func (s *Store) DeleteExpiredLogs(ctx context.Context, retention time.Duration) (int64, error)

DeleteExpiredLogs deletes all log entries older than the given retention duration. Returns the number of log batches deleted.

func (*Store) DeleteTask

func (s *Store) DeleteTask(ctx context.Context, id TaskID) error

DeleteTask deletes a task, its logs, and removes it from any other tasks' dependency lists.

func (*Store) FeedbackRetryTask

func (s *Store) FeedbackRetryTask(ctx context.Context, id TaskID, feedback string) error

FeedbackRetryTask transitions a task in review back to pending so the agent can iterate on its solution based on the user's feedback. Unlike ManualRetryTask, it preserves the existing PR/branch so the agent pushes fixes to the same branch.

Feedback retries (manual change requests) do not count towards the max retry attempts because they represent user-driven iteration rather than failure recovery. Both attempt and max_attempts are incremented so the attempt number is unique for log tabbing while keeping the retry budget unchanged.

func (*Store) GetAgentMetrics

func (s *Store) GetAgentMetrics(ctx context.Context) (*AgentMetrics, error)

GetAgentMetrics computes agent observability metrics from current task data.

func (*Store) HasTasksForRepo

func (s *Store) HasTasksForRepo(ctx context.Context, repoID string) (bool, error)

HasTasksForRepo checks whether any tasks exist for a given repo.

func (*Store) Heartbeat

func (s *Store) Heartbeat(ctx context.Context, id TaskID) (bool, error)

Heartbeat updates the last heartbeat time for a running task. Returns true if the task is still running, false if it was stopped, closed, or deleted — signalling the worker to cancel execution.

func (*Store) ListTasks

func (s *Store) ListTasks(ctx context.Context) ([]*Task, error)

ListTasks returns all tasks.

func (*Store) ListTasksByEpic

func (s *Store) ListTasksByEpic(ctx context.Context, epicID string) ([]*Task, error)

ListTasksByEpic returns all tasks belonging to a given epic.

func (*Store) ListTasksByRepo

func (s *Store) ListTasksByRepo(ctx context.Context, repoID string) ([]*Task, error)

ListTasksByRepo returns all tasks for a given repo.

func (*Store) ListTasksInReview

func (s *Store) ListTasksInReview(ctx context.Context) ([]*Task, error)

ListTasksInReview returns all tasks in review status.

func (*Store) ListTasksInReviewByRepo

func (s *Store) ListTasksInReviewByRepo(ctx context.Context, repoID string) ([]*Task, error)

ListTasksInReviewByRepo returns tasks in review status for a given repo.

func (*Store) ListTasksInReviewNoPR

func (s *Store) ListTasksInReviewNoPR(ctx context.Context) ([]*Task, error)

ListTasksInReviewNoPR returns tasks in review that have a branch but no PR yet.

func (*Store) ManualRetryTask

func (s *Store) ManualRetryTask(ctx context.Context, id TaskID, instructions string) error

ManualRetryTask transitions a failed task back to pending for another attempt. instructions contains optional guidance for the agent on the retry. Previous attempt logs are preserved — the UI shows them in separate tabs.

func (*Store) MoveToReview

func (s *Store) MoveToReview(ctx context.Context, id TaskID) error

MoveToReview transitions a failed task back to review status. This is only allowed when the task has a PR or branch from a previous attempt — the user wants to treat the existing PR as reviewable despite the agent failure.

func (*Store) ReadTask

func (s *Store) ReadTask(ctx context.Context, id TaskID) (*Task, error)

ReadTask reads a task by ID.

func (*Store) ReadTaskLogs

func (s *Store) ReadTaskLogs(ctx context.Context, id TaskID) ([]string, error)

ReadTaskLogs reads all logs for a task.

func (*Store) ReadTaskStatus

func (s *Store) ReadTaskStatus(ctx context.Context, idStr string) (string, error)

ReadTaskStatus reads a task's status by its string ID.

func (*Store) RemoveDependency

func (s *Store) RemoveDependency(ctx context.Context, id TaskID, depID string) error

RemoveDependency removes a dependency from a task. The dependency ID must be a valid task ID. After removal, if the task is pending, a pending notification is sent in case the task is now unblocked.

func (*Store) RetryTask

func (s *Store) RetryTask(ctx context.Context, id TaskID, category, reason string) error

RetryTask transitions a task from review back to pending for another attempt. category classifies the failure type (e.g. "ci_failure:tests", "merge_conflict") for circuit breaker detection. If the same category fails consecutively >= 3 times, the task is failed immediately. Categories include the specific failed check names so that different CI failures don't trip the breaker.

Merge conflict retries are exempt from the max attempts limit and circuit breaker because resolving conflicts can be an ongoing process when there is a lot of in-flight work happening on the same repo. Like feedback retries, both attempt and max_attempts are incremented so the attempt number stays unique for log tabbing while keeping the retry budget unchanged.

func (*Store) ScheduleRetry

func (s *Store) ScheduleRetry(ctx context.Context, id TaskID, reason string) error

ScheduleRetry transitions a running task back to pending for another attempt. This is used when the agent hits a retryable error such as Claude rate limits or session max usage exceeded. The task keeps its existing PR/branch info so the next attempt can continue where the previous one left off.

func (*Store) SetAgentStatus

func (s *Store) SetAgentStatus(ctx context.Context, id TaskID, status string) error

SetAgentStatus stores the structured agent status JSON.

func (*Store) SetCloseReason

func (s *Store) SetCloseReason(ctx context.Context, id TaskID, reason string) error

SetCloseReason sets the close/failure reason on a task without changing its status.

func (*Store) SetPlanningEpicLister

func (s *Store) SetPlanningEpicLister(lister PlanningEpicLister)

SetPlanningEpicLister sets the PlanningEpicLister used by GetAgentMetrics to include epics that are actively being planned. This is set after construction to avoid circular dependencies.

func (*Store) SetReady

func (s *Store) SetReady(ctx context.Context, id TaskID, ready bool) error

SetReady updates the ready flag on a task. When a task is marked as ready and is pending, a notification is sent so workers can pick it up.

func (*Store) SetRetryContext

func (s *Store) SetRetryContext(ctx context.Context, id TaskID, retryCtx string) error

SetRetryContext stores detailed failure context (e.g. CI logs) for retries.

func (*Store) SetTaskBranch

func (s *Store) SetTaskBranch(ctx context.Context, id TaskID, branchName string) error

SetTaskBranch sets the branch name and moves the task to review status.

func (*Store) SetTaskPullRequest

func (s *Store) SetTaskPullRequest(ctx context.Context, id TaskID, prURL string, prNumber int) error

SetTaskPullRequest sets the PR URL and number, moving the task to review status.

func (*Store) StartOverTask

func (s *Store) StartOverTask(ctx context.Context, id TaskID, params StartOverTaskParams) (*Task, error)

StartOverTask resets a task from review, failed, or closed back to pending, clearing all metadata (logs, PR, branch, agent status, cost) and optionally updating the task details (title, description, acceptance criteria). Returns the task before reset so the caller can close the PR if needed.

func (*Store) StopTask

func (s *Store) StopTask(ctx context.Context, id TaskID, reason string) error

StopTask transitions a running task back to pending with ready=false, effectively interrupting the worker agent. The task won't be picked up again until the user manually retries it.

func (*Store) StreamTaskLogs

func (s *Store) StreamTaskLogs(ctx context.Context, id TaskID, fn func(attempt int, lines []string) error) error

StreamTaskLogs iterates log batches from the database one row at a time, calling fn for each batch. This avoids loading all logs into memory.

func (*Store) Subscribe

func (s *Store) Subscribe() chan Event

Subscribe returns a channel that receives task events.

func (*Store) TimeoutStaleTasks

func (s *Store) TimeoutStaleTasks(ctx context.Context, timeout time.Duration) (int, error)

TimeoutStaleTasks fails running tasks whose heartbeat has expired.

func (*Store) Unsubscribe

func (s *Store) Unsubscribe(ch chan Event)

Unsubscribe removes and closes a subscriber channel.

func (*Store) UpdatePendingTask

func (s *Store) UpdatePendingTask(ctx context.Context, id TaskID, params UpdatePendingTaskParams) error

UpdatePendingTask updates a pending task's editable fields. If the task is no longer in pending status, the update is rejected with a conflict error.

func (*Store) UpdateTaskStatus

func (s *Store) UpdateTaskStatus(ctx context.Context, id TaskID, status Status) error

UpdateTaskStatus updates a task's status.

func (*Store) WaitForPending

func (s *Store) WaitForPending() <-chan struct{}

WaitForPending returns a channel that signals when a pending task might be available.

type Task

type Task struct {
	ID                  TaskID     `json:"id"`
	RepoID              string     `json:"repo_id"`
	Title               string     `json:"title"`
	Description         string     `json:"description"`
	Status              Status     `json:"status"`
	Logs                []string   `json:"logs"`
	PullRequestURL      string     `json:"pull_request_url,omitempty"`
	PRNumber            int        `json:"pr_number,omitempty"`
	DependsOn           []string   `json:"depends_on,omitempty"`
	CloseReason         string     `json:"close_reason,omitempty"`
	Attempt             int        `json:"attempt"`
	MaxAttempts         int        `json:"max_attempts"`
	RetryReason         string     `json:"retry_reason,omitempty"`
	AcceptanceCriteria  []string   `json:"acceptance_criteria"`
	AgentStatus         string     `json:"agent_status,omitempty"`
	RetryContext        string     `json:"retry_context,omitempty"`
	ConsecutiveFailures int        `json:"consecutive_failures"`
	CostUSD             float64    `json:"cost_usd"`
	MaxCostUSD          float64    `json:"max_cost_usd,omitempty"`
	SkipPR              bool       `json:"skip_pr"`
	Ready               bool       `json:"ready"`
	EpicID              string     `json:"epic_id,omitempty"`
	Model               string     `json:"model,omitempty"`
	BranchName          string     `json:"branch_name,omitempty"`
	StartedAt           *time.Time `json:"started_at,omitempty"`
	DurationMs          *int64     `json:"duration_ms,omitempty"`
	CreatedAt           time.Time  `json:"created_at"`
	UpdatedAt           time.Time  `json:"updated_at"`
}

Task represents a unit of work dispatched to an AI coding agent.

func NewTask

func NewTask(repoID, title, description string, dependsOn, acceptanceCriteria []string, maxCostUSD float64, skipPR bool, model string, ready bool) *Task

NewTask creates a new Task with a generated TaskID and pending status.

func (*Task) ComputeDuration

func (t *Task) ComputeDuration()

ComputeDuration calculates the run duration from StartedAt to UpdatedAt for tasks that have finished running (review, merged, closed, failed), or from StartedAt to now for tasks that are currently running.

type TaskID

type TaskID struct {
	typeid.TypeID[taskPrefix]
}

TaskID is the unique identifier for a Task.

func MustParseTaskID

func MustParseTaskID(s string) TaskID

MustParseTaskID parses a string into a TaskID, panicking on failure.

func NewTaskID

func NewTaskID() TaskID

NewTaskID generates a new unique TaskID.

func ParseTaskID

func ParseTaskID(s string) (TaskID, error)

ParseTaskID parses a string into a TaskID.

type TaskRepository

type TaskRepository interface {
	CreateTask(ctx context.Context, task *Task) error
	ReadTask(ctx context.Context, id TaskID) (*Task, error)
	ListTasks(ctx context.Context) ([]*Task, error)
	ListTasksByRepo(ctx context.Context, repoID string) ([]*Task, error)
	ListPendingTasks(ctx context.Context) ([]*Task, error)
	ListPendingTasksByRepos(ctx context.Context, repoIDs []string) ([]*Task, error)
	AppendTaskLogs(ctx context.Context, id TaskID, attempt int, logs []string) error
	ReadTaskLogs(ctx context.Context, id TaskID) ([]string, error)
	StreamTaskLogs(ctx context.Context, id TaskID, fn func(attempt int, lines []string) error) error
	UpdateTaskStatus(ctx context.Context, id TaskID, status Status) error
	SetTaskPullRequest(ctx context.Context, id TaskID, prURL string, prNumber int) error
	ListTasksInReview(ctx context.Context) ([]*Task, error)
	ListTasksInReviewByRepo(ctx context.Context, repoID string) ([]*Task, error)
	CloseTask(ctx context.Context, id TaskID, reason string) error
	TaskExists(ctx context.Context, id TaskID) (bool, error)
	ReadTaskStatus(ctx context.Context, id TaskID) (Status, error)
	ClaimTask(ctx context.Context, id TaskID) (bool, error)
	HasTasksForRepo(ctx context.Context, repoID string) (bool, error)
	// RetryTask atomically transitions a task from review → pending, increments
	// attempt, and records the retry reason. Returns false if the task was not
	// in review status (already retried or status changed).
	RetryTask(ctx context.Context, id TaskID, reason string) (bool, error)
	// ScheduleRetryFromRunning atomically transitions a task from running → pending,
	// increments attempt, and records the retry reason. Used when the agent hits a
	// retryable error (e.g. Claude rate limit or session max usage exceeded).
	// Returns false if the task was not in running status.
	ScheduleRetryFromRunning(ctx context.Context, id TaskID, reason string) (bool, error)
	SetAgentStatus(ctx context.Context, id TaskID, status string) error
	SetRetryContext(ctx context.Context, id TaskID, retryCtx string) error
	AddCost(ctx context.Context, id TaskID, costUSD float64) error
	SetConsecutiveFailures(ctx context.Context, id TaskID, count int) error
	SetCloseReason(ctx context.Context, id TaskID, reason string) error
	SetBranchName(ctx context.Context, id TaskID, branchName string) error
	ListTasksInReviewNoPR(ctx context.Context) ([]*Task, error)
	ManualRetryTask(ctx context.Context, id TaskID, instructions string) (bool, error)
	// FeedbackRetryTask transitions a task from review → pending and records
	// the user's feedback. Unlike ManualRetryTask, it preserves the existing
	// PR/branch so the agent pushes fixes to the same branch. The attempt
	// counter is reset to 1 so that subsequent automated failure retries get
	// a fresh retry budget — failures after user-requested changes are
	// caused by those changes, not by the original code.
	FeedbackRetryTask(ctx context.Context, id TaskID, feedback string) (bool, error)
	DeleteTaskLogs(ctx context.Context, id TaskID) error
	RemoveDependency(ctx context.Context, id TaskID, depID string) error
	SetReady(ctx context.Context, id TaskID, ready bool) error
	// UpdatePendingTask atomically updates a pending task's editable fields.
	// Returns false if the task was not in pending status.
	UpdatePendingTask(ctx context.Context, id TaskID, params UpdatePendingTaskParams) (bool, error)
	// StartOverTask resets a task from review, failed, or closed back to pending with
	// fresh metadata. Clears logs, PR, branch, agent status, cost, and retry
	// state. Optionally updates title, description, and acceptance criteria.
	// Returns false if the task was not in review, failed, or closed status.
	StartOverTask(ctx context.Context, id TaskID, params StartOverTaskParams) (bool, error)
	// StopTask atomically transitions a task from running → pending with ready=false,
	// recording the stop reason. Returns false if the task was not in running status.
	StopTask(ctx context.Context, id TaskID, reason string) (bool, error)
	// Heartbeat updates the last heartbeat time for a running task.
	// Returns true if the task is still running (row was updated), false if the
	// task no longer exists or is no longer in running status (e.g. stopped,
	// closed, or deleted).
	Heartbeat(ctx context.Context, id TaskID) (bool, error)
	// ListStaleTasks returns running tasks whose last heartbeat is before the given time.
	ListStaleTasks(ctx context.Context, before time.Time) ([]*Task, error)
	DeleteTask(ctx context.Context, id TaskID) error
	// ListTasksByEpic returns all tasks belonging to a given epic.
	ListTasksByEpic(ctx context.Context, epicID string) ([]*Task, error)
	// BulkCloseTasksByEpic closes all non-terminal tasks for an epic.
	BulkCloseTasksByEpic(ctx context.Context, epicID, reason string) error
	// ClearEpicIDForTasks removes the epic_id FK from all tasks for a given epic.
	ClearEpicIDForTasks(ctx context.Context, epicID string) error
	// BulkDeleteTasksByEpic deletes all tasks (and their logs) for a given epic.
	BulkDeleteTasksByEpic(ctx context.Context, epicID string) error
	// BulkDeleteTasksByIDs deletes tasks (and their logs) by their IDs.
	BulkDeleteTasksByIDs(ctx context.Context, ids []string) error
	// DeleteExpiredLogs deletes all log entries older than the given time.
	// Returns the number of log batches deleted.
	DeleteExpiredLogs(ctx context.Context, before time.Time) (int64, error)
}

TaskRepository defines the data access methods for Tasks.

type UpdatePendingTaskParams

type UpdatePendingTaskParams struct {
	Title              string
	Description        string
	DependsOn          []string
	AcceptanceCriteria []string
	MaxCostUSD         float64
	SkipPR             bool
	Model              string
	Ready              bool
}

UpdatePendingTaskParams holds the fields that can be updated on a pending task. All fields are required — the caller should merge with current values before calling.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL