Documentation
¶
Overview ¶
Package issues provides a built-in issue tracking system for SpecLedger. Issues are stored as JSONL files per spec at specledger/<spec>/issues.jsonl.
Key features:
- Globally unique SHA-256 based issue IDs (SL-xxxxxx format)
- No daemon required - direct file I/O only
- File locking for concurrent access
- Migration support from Beads format
- Dependency tracking with cycle detection
- Definition of Done validation
Index ¶
- Constants
- Variables
- func CalculateCollisionProbability(n int) float64
- func CalculateSimilarity(s1, s2 string) float64
- func DetectCycles(trees []*DependencyTree) [][]string
- func DetectSpecContextFromPath(path string) (string, error)
- func FormatCycleWarning(cycles [][]string) string
- func FormatDuplicateWarning(duplicates []DuplicateResult) string
- func FormatNotFeatureBranchError(currentBranch string) string
- func GenerateIssueID(specContext, title string, createdAt time.Time) string
- func IsValidIssueID(id string) bool
- func IsValidIssueType(t IssueType) bool
- func IsValidLinkType(t LinkType) bool
- func IsValidStatus(s IssueStatus) bool
- func ParseIssueID(id string) (string, error)
- func ParseSpecFromBranch(branchName string) (string, bool)
- func ValidateSpecContext(specContext string) error
- type BeadsIssue
- type BeadsMigration
- type Blocker
- type CheckDuplicateResult
- type ChecklistItem
- type ContextDetector
- type DefinitionOfDone
- type DependencyTree
- type DuplicateResult
- type Issue
- type IssueStatus
- type IssueType
- type IssueUpdate
- type LinkType
- type ListFilter
- type MigrationResult
- type Migrator
- type MigratorOptions
- type ReadyIssue
- type RepairResult
- type SkippedLine
- type Store
- func (s *Store) AddDependency(fromID, toID string, linkType LinkType) error
- func (s *Store) Create(issue *Issue) error
- func (s *Store) Delete(id string) error
- func (s *Store) DetectCycles() ([][]string, error)
- func (s *Store) Get(id string) (*Issue, error)
- func (s *Store) GetBlockedIssues() ([]Issue, error)
- func (s *Store) GetBlockedIssuesWithBlockers() ([]ReadyIssue, error)
- func (s *Store) GetChildren(parentID string) ([]Issue, error)
- func (s *Store) GetDependencyTree(id string) (*DependencyTree, error)
- func (s *Store) GetHierarchyForest() ([]*DependencyTree, error)
- func (s *Store) List(filter ListFilter) ([]Issue, error)
- func (s *Store) ListReady(filter ListFilter) ([]ReadyIssue, error)
- func (s *Store) Path() string
- func (s *Store) RemoveDependency(fromID, toID string, linkType LinkType) error
- func (s *Store) Update(id string, update IssueUpdate) (*Issue, error)
- func (s *Store) WithLock(fn func() error) error
- func (s *Store) WithLockResult(fn func() (*Issue, error)) (*Issue, error)
- type StoreOptions
- type TreeRenderOptions
- type TreeRenderer
- func (r *TreeRenderer) FormatIssueSimple(issue Issue) string
- func (r *TreeRenderer) Render(tree *DependencyTree) string
- func (r *TreeRenderer) RenderForest(trees []*DependencyTree) string
- func (r *TreeRenderer) RenderHierarchyForest(rootLabel string, trees []*DependencyTree, totalIssues int) string
- func (r *TreeRenderer) RenderWithRoot(rootLabel string, trees []*DependencyTree, totalIssues int) string
Constants ¶
const DefaultSimilarityThreshold = 0.8
DefaultSimilarityThreshold is the default threshold for duplicate detection (80%)
Variables ¶
var ( ErrNotFeatureBranch = errors.New("not on a feature branch. Use --spec flag or checkout a ###-branch") ErrNoGitRepo = errors.New("not in a git repository") )
Context-related errors
var ( ErrCyclicDependency = errors.New("would create a circular dependency") ErrSelfDependency = errors.New("cannot create dependency on self") ErrDependencyNotFound = errors.New("dependency target issue not found") )
Dependency-related errors
var ( ErrInvalidIDFormat = errors.New("issue ID must be in format SL-xxxxxx (6 hex characters)") ErrIDPrefix = errors.New("issue ID must start with 'SL-'") )
ID-related errors
var ( ErrInvalidID = errors.New("issue ID must match format SL-xxxxxx") ErrInvalidTitle = errors.New("title is required and must be 1-200 characters") ErrInvalidStatus = errors.New("status must be one of: open, in_progress, closed") ErrInvalidPriority = errors.New("priority must be between 0 and 5") ErrInvalidIssueType = errors.New("issue type must be one of: epic, feature, task, bug") ErrInvalidSpecContext = errors.New("spec context must match pattern ###-name") )
Validation errors
var ( ErrBeadsNotFound = errors.New(".beads/issues.jsonl not found") ErrMigrationFailed = errors.New("migration failed") ErrNoIssuesToMigrate = errors.New("no issues to migrate") )
Migration-related errors
var ( ErrIssueNotFound = errors.New("issue not found") ErrIssueAlreadyExists = errors.New("issue already exists") ErrStoreLocked = errors.New("store is locked by another process") ErrSpecDirNotFound = errors.New("spec directory not found") )
Store-related errors
var NowFunc = time.Now
NowFunc is a variable for testing purposes
Functions ¶
func CalculateCollisionProbability ¶
CalculateCollisionProbability estimates the probability of at least one collision given n issues, using the birthday problem approximation. With 6 hex characters (16,777,216 possible values), this provides collision probability < 0.01% for up to 100,000 issues.
func CalculateSimilarity ¶
CalculateSimilarity calculates the Levenshtein similarity ratio between two strings
func DetectCycles ¶ added in v1.0.22
func DetectCycles(trees []*DependencyTree) [][]string
DetectCycles detects cycles in the dependency graph and returns them
func DetectSpecContextFromPath ¶
DetectSpecContextFromPath attempts to detect spec context from the current directory
func FormatCycleWarning ¶ added in v1.0.22
FormatCycleWarning formats a cycle warning message
func FormatDuplicateWarning ¶
func FormatDuplicateWarning(duplicates []DuplicateResult) string
FormatDuplicateWarning formats a warning message for duplicate issues
func FormatNotFeatureBranchError ¶
FormatNotFeatureBranchError returns a formatted error message with helpful context
func GenerateIssueID ¶
GenerateIssueID creates a deterministic, globally unique issue ID using SHA-256 hash of (spec_context + title + created_at). The ID format is SL-<6-char-hex> where the hex is the first 6 characters of the SHA-256 digest.
func IsValidIssueID ¶
IsValidIssueID checks if an ID is in valid format
func IsValidIssueType ¶
IsValidIssueType checks if an issue type is valid
func IsValidLinkType ¶
IsValidLinkType checks if a link type is valid
func IsValidStatus ¶
func IsValidStatus(s IssueStatus) bool
IsValidStatus checks if a status is valid
func ParseIssueID ¶
ParseIssueID validates an issue ID format and returns it if valid. Returns an error if the ID doesn't match the expected format. Note: Only lowercase hex characters are valid since GenerateIssueID always produces lowercase.
func ParseSpecFromBranch ¶
ParseSpecFromBranch extracts the spec context from a branch name Returns the spec context and true if valid, empty string and false otherwise
func ValidateSpecContext ¶
ValidateSpecContext validates a spec context string
Types ¶
type BeadsIssue ¶
type BeadsIssue struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Status string `json:"status"`
Priority int `json:"priority"`
Type string `json:"type"`
Labels []string `json:"labels,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ClosedAt string `json:"closed_at,omitempty"`
Notes string `json:"notes,omitempty"`
Design string `json:"design,omitempty"`
Acceptance string `json:"acceptance_criteria,omitempty"`
BlockedBy []string `json:"blocked_by,omitempty"`
Blocks []string `json:"blocks,omitempty"`
Assignee string `json:"assignee,omitempty"`
}
BeadsIssue represents the Beads JSONL format for issues
type BeadsMigration ¶
type BeadsMigration struct {
OriginalID string `json:"original_id"`
MigratedAt time.Time `json:"migrated_at"`
}
BeadsMigration contains metadata for issues migrated from Beads
type Blocker ¶ added in v1.0.22
type Blocker struct {
ID string `json:"id"`
Title string `json:"title"`
Status IssueStatus `json:"status"`
}
Blocker represents a blocking issue with its key details
type CheckDuplicateResult ¶
type CheckDuplicateResult struct {
HasDuplicates bool
Duplicates []DuplicateResult
}
CheckDuplicateResult contains the result of a duplicate check
func CheckDuplicatesAcrossAllSpecs ¶
func CheckDuplicatesAcrossAllSpecs(title string, threshold float64) (*CheckDuplicateResult, error)
CheckDuplicatesAcrossAllSpecs checks for duplicates across all specs
func CheckDuplicatesForCreate ¶
func CheckDuplicatesForCreate(title, specContext string, threshold float64) (*CheckDuplicateResult, error)
CheckDuplicatesForCreate checks for duplicates when creating a new issue
type ChecklistItem ¶
type ChecklistItem struct {
Item string `json:"item"`
Checked bool `json:"checked"`
VerifiedAt *time.Time `json:"verified_at,omitempty"`
}
ChecklistItem represents a single item in a definition of done checklist
type ContextDetector ¶
type ContextDetector struct {
// contains filtered or unexported fields
}
ContextDetector detects the current spec context from git branch
func NewContextDetector ¶
func NewContextDetector(repoPath string) *ContextDetector
NewContextDetector creates a new context detector
func (*ContextDetector) DetectSpecContext ¶
func (d *ContextDetector) DetectSpecContext() (string, error)
DetectSpecContext returns the current spec context from the git branch name
func (*ContextDetector) GetBranchName ¶
func (d *ContextDetector) GetBranchName() (string, error)
GetBranchName returns the current git branch name
func (*ContextDetector) IsFeatureBranch ¶
func (d *ContextDetector) IsFeatureBranch() (bool, error)
IsFeatureBranch checks if the current branch is a feature branch
type DefinitionOfDone ¶
type DefinitionOfDone struct {
Items []ChecklistItem `json:"items"`
}
DefinitionOfDone represents a checklist that must be completed before closing
func (*DefinitionOfDone) CheckItem ¶
func (d *DefinitionOfDone) CheckItem(itemText string) bool
CheckItem marks an item as checked
func (*DefinitionOfDone) GetUncheckedItems ¶
func (d *DefinitionOfDone) GetUncheckedItems() []string
GetUncheckedItems returns all unchecked items
func (*DefinitionOfDone) IsComplete ¶
func (d *DefinitionOfDone) IsComplete() bool
IsComplete returns true if all checklist items are checked
func (*DefinitionOfDone) UncheckItem ¶
func (d *DefinitionOfDone) UncheckItem(itemText string) bool
UncheckItem marks an item as unchecked
type DependencyTree ¶
type DependencyTree struct {
Issue Issue
BlockedBy []*DependencyTree
Blocks []*DependencyTree
Children []*DependencyTree // Parent-child hierarchy
}
DependencyTree represents the dependency tree for an issue
type DuplicateResult ¶
DuplicateResult contains information about a potential duplicate
func FindSimilarIssues ¶
func FindSimilarIssues(title string, issues []Issue, threshold float64) []DuplicateResult
FindSimilarIssues finds issues with similar titles to the given title
func FindSimilarIssuesAcrossSpecs ¶
func FindSimilarIssuesAcrossSpecs(title string, store *Store, threshold float64) ([]DuplicateResult, error)
FindSimilarIssuesAcrossSpecs finds similar issues across all specs
type Issue ¶
type Issue struct {
// Required fields
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Status IssueStatus `json:"status"`
Priority int `json:"priority"` // 0=highest, 5=lowest
IssueType IssueType `json:"issue_type"`
SpecContext string `json:"spec_context"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Optional fields
ClosedAt *time.Time `json:"closed_at,omitempty"`
DefinitionOfDone *DefinitionOfDone `json:"definition_of_done,omitempty"`
BlockedBy []string `json:"blocked_by,omitempty"` // Issue IDs
Blocks []string `json:"blocks,omitempty"` // Issue IDs
Labels []string `json:"labels,omitempty"`
Assignee string `json:"assignee,omitempty"`
Notes string `json:"notes,omitempty"`
Design string `json:"design,omitempty"`
AcceptanceCriteria string `json:"acceptance_criteria,omitempty"`
ParentID *string `json:"parentId,omitempty"` // Parent issue ID
// Migration metadata (optional, for Beads migration)
BeadsMigration *BeadsMigration `json:"beads_migration,omitempty"`
}
Issue represents a tracking unit for work items
func GetIssueAcrossSpecs ¶
GetIssueAcrossSpecs searches for an issue across all specs
func ListAllSpecs ¶
func ListAllSpecs(basePath string, filter ListFilter) ([]Issue, error)
ListAllSpecs lists issues across all spec directories
func (*Issue) GetBlockers ¶ added in v1.0.22
GetBlockers returns details about blocking issues for display purposes. Returns a slice of Blocker structs with ID, Title, and Status.
type IssueStatus ¶
type IssueStatus string
IssueStatus represents the current state of an issue
const ( StatusOpen IssueStatus = "open" StatusInProgress IssueStatus = "in_progress" StatusClosed IssueStatus = "closed" )
type IssueUpdate ¶
type IssueUpdate struct {
Title *string
Description *string
Status *IssueStatus
Priority *int
IssueType *IssueType
Assignee *string
Notes *string
Design *string
AcceptanceCriteria *string
Labels *[]string
AddLabels []string
RemoveLabels []string
BlockedBy *[]string
Blocks *[]string
DefinitionOfDone *DefinitionOfDone
CheckDoDItem string // Item to mark as checked
UncheckDoDItem string // Item to mark as unchecked
ParentID *string // Set or clear parent
}
IssueUpdate represents partial updates to an issue
type ListFilter ¶
type ListFilter struct {
Status *IssueStatus
IssueType *IssueType
Priority *int
Labels []string
SpecContext string // Empty = all specs
All bool // Search across all specs
Blocked bool // Only show blocked issues
}
ListFilter represents filtering options for listing issues
type MigrationResult ¶
type MigrationResult struct {
TotalIssues int
MigratedIssues int
SkippedIssues int
SpecDistribution map[string]int // spec -> count
UnmappedIssues []BeadsIssue
Errors []error
Warnings []string
}
MigrationResult contains the results of a migration
type Migrator ¶
type Migrator struct {
// contains filtered or unexported fields
}
Migrator handles migration from Beads to sl issue format
func NewMigrator ¶
func NewMigrator(opts MigratorOptions) *Migrator
NewMigrator creates a new migrator
func (*Migrator) GetIDMapping ¶
GetIDMapping returns the ID mapping from old Beads IDs to new SL IDs
func (*Migrator) Migrate ¶
func (m *Migrator) Migrate() (*MigrationResult, error)
Migrate performs the full migration from Beads to sl issue format
type MigratorOptions ¶
type MigratorOptions struct {
BeadsPath string // Path to .beads directory (default: ".beads")
ArtifactPath string // Path to specledger directory (default: "specledger")
DryRun bool
KeepBeads bool
}
MigratorOptions contains options for the migrator
type ReadyIssue ¶ added in v1.0.22
type ReadyIssue struct {
Issue Issue `json:"issue"`
BlockedBy []Blocker `json:"blocked_by,omitempty"` // Empty for ready issues
}
ReadyIssue represents an issue that is ready to work on
func ListReadyAcrossSpecs ¶ added in v1.0.22
func ListReadyAcrossSpecs(basePath string, filter ListFilter) ([]ReadyIssue, error)
ListReadyAcrossSpecs returns ready issues across all spec directories.
type RepairResult ¶
type RepairResult struct {
ValidLines int
InvalidLines int
RecoveredIssues int
SkippedLines []SkippedLine
BackupPath string
}
RepairResult contains the result of repairing an issues file
func RepairIssuesFile ¶
func RepairIssuesFile(path string) (*RepairResult, error)
RepairIssuesFile repairs a corrupted issues.jsonl file
type SkippedLine ¶
SkippedLine contains information about a skipped line during repair
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store manages JSONL file operations with file locking
func NewStore ¶
func NewStore(opts StoreOptions) (*Store, error)
NewStore creates a new issue store for a specific spec context
func (*Store) AddDependency ¶
AddDependency creates a dependency link between two issues
func (*Store) DetectCycles ¶
DetectCycles checks for circular dependencies in the entire issue set
func (*Store) GetBlockedIssues ¶
GetBlockedIssues returns all issues that are currently blocked
func (*Store) GetBlockedIssuesWithBlockers ¶ added in v1.0.22
func (s *Store) GetBlockedIssuesWithBlockers() ([]ReadyIssue, error)
GetBlockedIssuesWithBlockers returns all issues that are currently blocked, along with their blocker details.
func (*Store) GetChildren ¶ added in v1.0.29
GetChildren returns all issues that have the given issue as parent Results are ordered by priority (descending), then by ID (ascending)
func (*Store) GetDependencyTree ¶
func (s *Store) GetDependencyTree(id string) (*DependencyTree, error)
GetDependencyTree returns the full dependency tree for an issue
func (*Store) GetHierarchyForest ¶ added in v1.0.29
func (s *Store) GetHierarchyForest() ([]*DependencyTree, error)
GetHierarchyForest returns a forest of trees based on parent-child relationships. Root nodes are issues without a parent. Each tree includes all descendants.
func (*Store) List ¶
func (s *Store) List(filter ListFilter) ([]Issue, error)
List returns issues matching the filter
func (*Store) ListReady ¶ added in v1.0.22
func (s *Store) ListReady(filter ListFilter) ([]ReadyIssue, error)
ListReady returns all issues that are ready to work on (not blocked by open dependencies). Ready issues have status open or in_progress and all their blockers are closed.
func (*Store) RemoveDependency ¶
RemoveDependency removes a dependency link between two issues
func (*Store) Update ¶
func (s *Store) Update(id string, update IssueUpdate) (*Issue, error)
Update updates an existing issue
type StoreOptions ¶
type StoreOptions struct {
BasePath string // Base path to specledger directory (default: "specledger")
SpecContext string // Spec context (e.g., "010-my-feature"), empty for cross-spec mode
}
StoreOptions contains options for creating a new Store
type TreeRenderOptions ¶ added in v1.0.22
type TreeRenderOptions struct {
MaxDepth int // Maximum depth to render (default: 10)
ShowStatus bool // Include status indicator (default: true)
TitleWidth int // Max title width before truncation (default: 40)
ShowSpec bool // Show spec context for cross-spec trees (default: false)
ShowType bool // Show issue type (default: true)
ShowPriority bool // Show priority (default: true)
Color bool // Use colors (default: true)
}
TreeRenderOptions configures the tree output format
func DefaultTreeRenderOptions ¶ added in v1.0.22
func DefaultTreeRenderOptions() TreeRenderOptions
DefaultTreeRenderOptions returns the default options
type TreeRenderer ¶ added in v1.0.22
type TreeRenderer struct {
// contains filtered or unexported fields
}
TreeRenderer handles tree output formatting
func NewTreeRenderer ¶ added in v1.0.22
func NewTreeRenderer(opts TreeRenderOptions) *TreeRenderer
NewTreeRenderer creates a new tree renderer with the given options
func (*TreeRenderer) FormatIssueSimple ¶ added in v1.0.22
func (r *TreeRenderer) FormatIssueSimple(issue Issue) string
FormatIssueSimple is a public method to format an issue for display
func (*TreeRenderer) Render ¶ added in v1.0.22
func (r *TreeRenderer) Render(tree *DependencyTree) string
Render renders a single dependency tree
func (*TreeRenderer) RenderForest ¶ added in v1.0.22
func (r *TreeRenderer) RenderForest(trees []*DependencyTree) string
RenderForest renders multiple dependency trees
func (*TreeRenderer) RenderHierarchyForest ¶ added in v1.0.29
func (r *TreeRenderer) RenderHierarchyForest(rootLabel string, trees []*DependencyTree, totalIssues int) string
RenderHierarchyForest renders a forest of hierarchy trees (parent-child)
func (*TreeRenderer) RenderWithRoot ¶ added in v1.0.22
func (r *TreeRenderer) RenderWithRoot(rootLabel string, trees []*DependencyTree, totalIssues int) string
RenderWithRoot renders a tree with a root label