issue

package
v0.2.0-alpha.13 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const CommentBodyLimit = 16 * 1024

CommentBodyLimit bounds a comment body in bytes.

A comment is a statement about an issue, not a place to paste a run's output: long content belongs in an artifact, and a comment points at one. The limit also bounds the size of a thread response, which is otherwise unbounded in the number of comments times their length.

Variables

View Source
var (
	ErrCommentsNotConfigured = apierr.New(apierr.KindNotConfigured, "comments not configured")
	ErrCommentBodyRequired   = apierr.New(apierr.KindInvalid, "comment body required")
	ErrCommentTooLong        = apierr.New(apierr.KindInvalid, "comment too long")
	ErrCommentNotFound       = apierr.New(apierr.KindNotFound, "comment not found")
	ErrCommentNotEditable    = apierr.New(apierr.KindForbidden, "comment not editable")
	// ErrInvalidCommentAuthorKind refuses an author kind the thread has no
	// rendering for. A kind nobody displays is a comment nobody can attribute.
	ErrInvalidCommentAuthorKind = apierr.New(apierr.KindInvalid, "invalid author_kind")
)
View Source
var (
	ErrIssuesNotConfigured = apierr.New(apierr.KindNotConfigured, "issues not configured")
	ErrSpacesNotConfigured = apierr.New(apierr.KindNotConfigured, "spaces not configured")
	ErrTitleRequired       = apierr.New(apierr.KindInvalid, "title required")
	ErrInvalidStatus       = apierr.New(apierr.KindInvalid, "invalid status")
	ErrInvalidOwnerID      = apierr.New(apierr.KindInvalid, "invalid owner_id")
	ErrInvalidExecutorKind = apierr.New(apierr.KindInvalid, "invalid executor_kind")
	ErrInvalidExecutorID   = apierr.New(apierr.KindInvalid, "invalid executor_id")
	ErrIssueNotFound       = apierr.New(apierr.KindNotFound, "issue not found")
	// ErrVersionRequired means the caller sent no precondition. Refusing is the
	// point: an update with no version is the unconditional overwrite this
	// contract exists to remove, so it cannot be the permissive default.
	ErrVersionRequired        = apierr.New(apierr.KindInvalid, "version is required: read the issue and send the version it returned")
	ErrAgentsNotConfigured    = apierr.New(apierr.KindNotConfigured, "agents not configured")
	ErrAgentNotFound          = apierr.New(apierr.KindInvalid, "agent not found")
	ErrWorkflowsNotConfigured = apierr.New(apierr.KindNotConfigured, "workflows not configured")
	ErrWorkflowNotFound       = apierr.New(apierr.KindInvalid, "workflow not found")
	ErrWorkflowNotPublished   = apierr.New(apierr.KindInvalid, "workflow not published")
	// ErrParentNotFound covers both a parent that does not exist and one that
	// belongs to another space. The two are reported identically on purpose:
	// distinguishing them would confirm that an issue ID exists somewhere the
	// caller cannot see, and issue IDs are what Portal puts in URLs.
	ErrParentNotFound   = apierr.New(apierr.KindInvalid, "parent issue not found")
	ErrHierarchyTooDeep = apierr.New(apierr.KindInvalid, "issue hierarchy too deep")
	ErrIssueHasChildren = apierr.New(apierr.KindInvalid, "issue has sub-issues")
	ErrInvalidParent    = apierr.New(apierr.KindInvalid, "invalid parent_issue_id")
)
View Source
var (
	// ErrNotAssignedToAgent means the Issue names no Agent to start.
	ErrNotAssignedToAgent = apierr.New(apierr.KindInvalid, "issue not assigned to agent")
	// ErrNotAssignedToWorkflow means the Issue names no Workflow to start.
	ErrNotAssignedToWorkflow = apierr.New(apierr.KindInvalid, "issue not assigned to workflow")
	// ErrAssignedAgentGone means the Issue names an Agent the space no longer
	// has. An assignment outlives a deletion, so this is reachable.
	ErrAssignedAgentGone = apierr.New(apierr.KindInvalid, "agent not found")
)

Refusals starting an Issue's assigned work can produce.

Functions

func IsRefusal

func IsRefusal(err error) bool

IsRefusal reports whether err is one of this package's refusals rather than a failure, so a transport can tell the two apart without listing them.

Types

type Admitter

type Admitter interface {
	Admits(ctx context.Context, spaceID string) error
}

Admitter reports whether a space may start one more background run.

It is asked before anything is written. The task service checks the same allowance when it creates the task; this early check keeps admission failures ahead of persistence.

type Counts

type Counts struct {
	Children     int
	DoneChildren int
	Comments     int
}

Counts are the derived numbers a list of issues carries: how many sub-issues each has, how many are done, how many comments.

type CreateCommentCmd

type CreateCommentCmd struct {
	IssueID         string
	AuthorKind      string
	AuthorID        string
	Body            string
	SourceTaskID    *string
	SourceTaskRunID *string
}

type CreateIssueCmd

type CreateIssueCmd struct {
	UserID        string
	SpaceID       string
	Title         string
	Description   string
	ParentIssueID *string
}

type DeleteCommentCmd

type DeleteCommentCmd struct {
	IssueID     string
	CommentID   string
	UserID      string
	CanModerate bool
}

type RunReporter

type RunReporter struct {
	Tasks    coretask.Store
	Comments coreissue.CommentStore
}

RunReporter posts an agent comment on the issue a finished task run belongs to.

It is driven by the terminal-run callback, which fires after the worker has already been answered. Every failure path here returns without an error reaching that response: a comment that could not be written must not turn a completed run into a failed one.

func (*RunReporter) ReportRunTerminal

func (r *RunReporter) ReportRunTerminal(ctx context.Context, info coretask.RunTerminalInfo) error

ReportRunTerminal writes one comment for a run that reached a terminal status, and nothing at all when there is no issue, no store, or nothing to say.

One comment per terminal run is the whole budget. A run that streamed for twenty minutes still produces one line in the thread.

type Service

type Service struct {
	Issues    coreissue.Store
	Comments  coreissue.CommentStore
	Agents    agentdef.Store
	Spaces    corespace.Store
	Workflows coreworkflow.Store
}

func (*Service) AssignedWorkflowID

func (s *Service) AssignedWorkflowID(ctx context.Context, spaceID, issueID string) (*coreissue.Issue, string, error)

AssignedWorkflowID validates that the Issue names a Workflow to start and reports which, so the caller does not repeat the assignment rules the workflow service would otherwise check a second time.

func (*Service) CountsFor

func (s *Service) CountsFor(ctx context.Context, issueIDs []string) map[string]Counts

CountsFor loads the derived counts for a page of issues with one grouped query each, rather than a count per row.

A failure degrades to zero for that count instead of failing the page: a missing progress badge is a worse-looking list, an error is no list at all. Both stores are optional, and a deployment without one simply reports zero.

func (*Service) CreateComment

func (s *Service) CreateComment(ctx context.Context, cmd CreateCommentCmd) (*coreissue.Comment, error)

CreateComment appends a comment to an issue. The caller is responsible for having authorized the issue's space.

func (*Service) CreateIssue

func (s *Service) CreateIssue(ctx context.Context, cmd CreateIssueCmd) (*coreissue.Issue, error)

func (*Service) DeleteComment

func (s *Service) DeleteComment(ctx context.Context, cmd DeleteCommentCmd) error

DeleteComment removes a comment. The author may delete their own; a moderator may delete any comment on the issue, including one an agent wrote.

func (*Service) GetIssue

func (s *Service) GetIssue(ctx context.Context, spaceID, issueID string) (*coreissue.Issue, error)

GetIssue resolves an issue the space owns.

An issue belonging to another space reads as not found rather than forbidden, so the answer does not confirm that an id exists elsewhere.

func (*Service) ListChildren

func (s *Service) ListChildren(ctx context.Context, spaceID, issueID string) ([]coreissue.Issue, error)

ListChildren returns one issue's sub-issues, oldest first, after checking the parent belongs to the space. Callers that already hold the parent still go through here: the space check is the authorization, not a convenience.

func (*Service) ListComments

func (s *Service) ListComments(ctx context.Context, issueID string, limit, offset int) ([]coreissue.Comment, int, error)

ListComments returns an issue's thread, oldest first.

func (*Service) ListIssues

func (s *Service) ListIssues(ctx context.Context, spaceID string, filter coreissue.ListFilter, limit, offset int) ([]coreissue.Issue, int, error)

func (*Service) PlanAssignedAgentRun

func (s *Service) PlanAssignedAgentRun(
	ctx context.Context,
	cmd StartAssignedAgentCmd,
	admitter Admitter,
) (*StartAssignedAgentPlan, error)

PlanAssignedAgentRun validates the Issue and its assignment, asks whether the space may start a run, and only then opens the conversation.

The order is the point. Validating after the write leaves a bad request with a conversation attached to it; opening the conversation before asking about the allowance leaves one behind on every refusal, which a space at its run limit hits on every attempt.

func (*Service) UpdateComment

func (s *Service) UpdateComment(ctx context.Context, cmd UpdateCommentCmd) (*coreissue.Comment, error)

UpdateComment replaces a comment's body.

Only the person who wrote a comment may edit it. Moderation permits deletion, not rewriting: an edit puts words in another person's mouth, and an agent or system comment is the record of what a run reported — a record anyone can rewrite is not one.

func (*Service) UpdateIssue

func (s *Service) UpdateIssue(ctx context.Context, cmd UpdateIssueCmd) (*coreissue.Issue, error)

type StartAssignedAgentCmd

type StartAssignedAgentCmd struct {
	SpaceID string
	IssueID string
	UserID  string
	// Input overrides what the Agent is asked. Empty means the Issue itself.
	Input string
}

StartAssignedAgentCmd starts the Agent an Issue is assigned to.

type StartAssignedAgentPlan

type StartAssignedAgentPlan struct {
	Issue   coreissue.Issue
	AgentID string
}

StartAssignedAgentPlan is a validated, admitted start: what to run, on what input.

type UpdateCommentCmd

type UpdateCommentCmd struct {
	IssueID   string
	CommentID string
	UserID    string
	Body      string
	// CanModerate is true when the caller may edit or delete a comment they did
	// not write. Only deletion honors it; see UpdateComment.
	CanModerate bool
}

type UpdateIssueCmd

type UpdateIssueCmd struct {
	UserID  string
	SpaceID string
	IssueID string
	// IfVersion is the Version the caller read. See coreissue.UpdateInput.
	IfVersion     uint64
	Title         *string
	Description   *string
	Status        *string
	OwnerID       *string
	ExecutorKind  *string
	ExecutorID    *string
	ParentIssueID *string
}

Jump to

Keyboard shortcuts

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