core

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package core implements the driving port interface defined in internal/ports/driving. It contains the business logic that orchestrates domain operations through the driven port (Transactor) without knowledge of the specific storage or transport adapters in use.

Driving adapters (CLI commands, HTTP handlers) depend on the driving port interface, not on this package directly. The configurator (internal/wiring) wires core to the driven adapter at startup.

Index

Constants

View Source
const MaxDepth = 3

MaxDepth is the maximum number of levels allowed in the issue hierarchy. A root issue is level 1, its child is level 2, its grandchild is level 3.

Variables

This section is empty.

Functions

func CountSkippedChecks

func CountSkippedChecks(checks []driving.DoctorCheckResult) int

CountSkippedChecks returns the number of checks with status "skipped".

func EpicSecondaryState

func EpicSecondaryState(
	state domain.State,
	hasChildren bool,
	allChildrenClosed bool,
	blockers []domain.BlockerStatus,
	ancestors []domain.AncestorStatus,
) domain.SecondaryStateResult

EpicSecondaryState computes the secondary state for an epic based on its primary state, child status, blockers, and ancestor conditions.

The function applies list-view priority rules: completed > blocked > ready > active. Detail-view states capture the full set of applicable conditions (e.g., [blocked, active] for a blocked epic with in-progress children).

Returns a zero-value SecondaryStateResult (ListState = SecondaryNone) for claimed and closed epics, or for deferred epics that are not blocked.

func IsEpicReady

func IsEpicReady(state domain.State, hasChildren bool, blockers []domain.BlockerStatus, ancestors []domain.AncestorStatus) bool

IsEpicReady determines whether an epic is ready for decomposition.

An epic is ready when:

  1. Its state is open.
  2. It has no children (needs decomposition).
  3. It has no unresolved blocked_by relationships.
  4. No ancestor is deferred or blocked.

func IsTaskReady

func IsTaskReady(state domain.State, blockers []domain.BlockerStatus, ancestors []domain.AncestorStatus) bool

IsTaskReady determines whether a task is ready for work.

A task is ready when:

  1. Its state is open.
  2. It has no unresolved blocked_by relationships (closed or deleted targets count as resolved).
  3. No ancestor is deferred or blocked.

func New

New creates a new driving.Service backed by the given Transactor.

func SeverityBelow

func SeverityBelow(threshold driving.DoctorSeverity) string

SeverityBelow returns the label for the severity level immediately below the given threshold. Used in skip summary messages.

func StealComment

func StealComment(previousHolder string) string

StealComment generates the auto-comment body added when an issue is stolen.

func TaskSecondaryState

func TaskSecondaryState(state domain.State, blockers []domain.BlockerStatus, ancestors []domain.AncestorStatus) domain.SecondaryStateResult

TaskSecondaryState computes the secondary state for a task given its primary state, blockers, and ancestor statuses.

Rules:

  • open + not blocked → ready
  • open + blocked (unresolved blockers or blocked/deferred ancestor) → blocked
  • deferred + blocked → blocked
  • deferred + not blocked → none
  • claimed → none
  • closed → none

func ValidateClaim

func ValidateClaim(status IssueClaimStatus, allowSteal bool, now time.Time) error

ValidateClaim checks whether an issue can be claimed per §6.1. Returns nil if the issue is claimable, or an appropriate error.

func ValidateDeletion

func ValidateDeletion(isDeleted bool) error

ValidateDeletion checks whether an issue can be deleted. An issue must not already be deleted.

func ValidateDepth

func ValidateDepth(proposedParent domain.ID, lookup AncestorLookup) error

ValidateDepth checks that assigning a child under proposedParent would not exceed MaxDepth levels. It walks the ancestor chain of proposedParent to determine how deep it sits, then verifies room for one more level.

func ValidateEpicDepth

func ValidateEpicDepth(role domain.Role, proposedParent domain.ID, lookup AncestorLookup) error

ValidateEpicDepth checks that creating an epic under proposedParent would not place it at MaxDepth. Epics organize children, but an issue at MaxDepth cannot have children — so an epic there is structurally invalid. Tasks are permitted at MaxDepth because they are leaf nodes.

This validation is additive to ValidateDepth; callers should invoke both. ValidateDepth rejects any role at depth > MaxDepth, while ValidateEpicDepth further restricts epics to depth < MaxDepth.

func ValidateNoCycle

func ValidateNoCycle(childID, proposedParent domain.ID, lookup AncestorLookup) error

ValidateNoCycle walks the ancestor chain of proposedParent to ensure that assigning it as the parent of childID would not create a cycle. A cycle exists if childID appears as an ancestor of proposedParent.

func ValidateParent

func ValidateParent(childID, parentID domain.ID, parentDeleted bool) error

ValidateParent checks parent assignment constraints:

  • An issue cannot be its own parent.
  • A deleted issue cannot be assigned as a parent.

Any issue role (task or epic) may be a parent of any other issue role. Cycle detection is handled separately by ValidateNoCycle.

Types

type AncestorLookup

type AncestorLookup func(id domain.ID) (parentID domain.ID, err error)

AncestorLookup is a callback that returns the parent ID of a given issue. It returns a zero ID if the issue has no parent. An error indicates a lookup failure.

type DeletionResult

type DeletionResult struct {
	// ToDelete contains the IDs of all issues that should be soft-deleted,
	// including the target issue itself.
	ToDelete []domain.ID

	// Conflicts contains descendants that are currently claimed and prevent
	// the deletion.
	Conflicts []domain.DescendantInfo
}

DeletionResult holds the outcome of a deletion check: either a set of issue IDs to delete or a conflict error identifying claimed descendants.

func PlanEpicDeletion

func PlanEpicDeletion(epicID domain.ID, descendants []domain.DescendantInfo) DeletionResult

PlanEpicDeletion checks whether an epic can be deleted by examining all its descendants. If any descendant is currently claimed, the deletion fails with a conflict listing the claimed issue(s). Otherwise, it returns the set of issue IDs to soft-delete (the epic itself plus all unclaimed descendants).

For tasks, the result contains only the task's own ID (tasks have no descendants).

type EpicProgress

type EpicProgress struct {
	// Total is the number of direct children.
	Total int
	// Closed is the number of children in the closed state.
	Closed int
	// Claimed is the number of children in the claimed state.
	Claimed int
	// Open is the number of non-blocked children in the open state.
	Open int
	// Blocked is the number of children that are blocked (any primary state
	// with an unresolved blocked_by relationship).
	Blocked int
	// Deferred is the number of non-blocked children in the deferred state.
	Deferred int
	// Percent is the completion percentage (0–100).
	Percent int
	// Completed is true when the epic has at least one child and all
	// children are closed.
	Completed bool
}

EpicProgress holds the computed completion metrics for an epic.

func ComputeEpicProgress

func ComputeEpicProgress(children []domain.ChildStatus) EpicProgress

ComputeEpicProgress derives completion metrics from a list of child statuses. An epic is completed when it has at least one child and all children are closed. Returns a zero-value EpicProgress when the child list is empty.

Blocked children are counted separately regardless of their primary state. A claimed child that is also blocked counts as blocked, not claimed.

type IssueClaimStatus

type IssueClaimStatus struct {
	// State is the issue's current lifecycle state.
	State domain.State
	// IsDeleted is true if the issue has been soft-deleted.
	IsDeleted bool
	// ActiveClaim is the current claim on the issue, if any. A zero-value
	// Claim (empty ID) indicates no active claim.
	ActiveClaim domain.Claim
}

IssueClaimStatus summarizes an issue's state for claim validation.

Jump to

Keyboard shortcuts

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