Initial commit: Beads issue tracker with security fixes

Core features:
- Dependency-aware issue tracking with SQLite backend
- Ready work detection (issues with no open blockers)
- Dependency tree visualization
- Cycle detection and prevention
- Full audit trail
- CLI with colored output

Security and correctness fixes applied:
- Fixed SQL injection vulnerability in UpdateIssue (whitelisted fields)
- Fixed race condition in ID generation (added mutex)
- Fixed cycle detection to return full paths (not just issue IDs)
- Added cycle prevention in AddDependency (validates before commit)
- Added comprehensive input validation (priority, status, types, etc.)
- Fixed N+1 query in GetBlockedIssues (using GROUP_CONCAT)
- Improved query building in GetReadyWork (proper string joining)
- Fixed P0 priority filter bug (using Changed() instead of value check)

All critical and major issues from code review have been addressed.

🤖 Generated with Claude Code
This commit is contained in:
Steve Yegge
2025-10-11 20:07:36 -07:00
commit 704515125d
19 changed files with 3976 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
package storage
import (
"context"
"github.com/steveyackey/beads/internal/types"
)
// Storage defines the interface for issue storage backends
type Storage interface {
// Issues
CreateIssue(ctx context.Context, issue *types.Issue, actor string) error
GetIssue(ctx context.Context, id string) (*types.Issue, error)
UpdateIssue(ctx context.Context, id string, updates map[string]interface{}, actor string) error
CloseIssue(ctx context.Context, id string, reason string, actor string) error
SearchIssues(ctx context.Context, query string, filter types.IssueFilter) ([]*types.Issue, error)
// Dependencies
AddDependency(ctx context.Context, dep *types.Dependency, actor string) error
RemoveDependency(ctx context.Context, issueID, dependsOnID string, actor string) error
GetDependencies(ctx context.Context, issueID string) ([]*types.Issue, error)
GetDependents(ctx context.Context, issueID string) ([]*types.Issue, error)
GetDependencyTree(ctx context.Context, issueID string, maxDepth int) ([]*types.TreeNode, error)
DetectCycles(ctx context.Context) ([][]*types.Issue, error)
// Labels
AddLabel(ctx context.Context, issueID, label, actor string) error
RemoveLabel(ctx context.Context, issueID, label, actor string) error
GetLabels(ctx context.Context, issueID string) ([]string, error)
GetIssuesByLabel(ctx context.Context, label string) ([]*types.Issue, error)
// Ready Work & Blocking
GetReadyWork(ctx context.Context, filter types.WorkFilter) ([]*types.Issue, error)
GetBlockedIssues(ctx context.Context) ([]*types.BlockedIssue, error)
// Events
AddComment(ctx context.Context, issueID, actor, comment string) error
GetEvents(ctx context.Context, issueID string, limit int) ([]*types.Event, error)
// Statistics
GetStatistics(ctx context.Context) (*types.Statistics, error)
// Lifecycle
Close() error
}
// Config holds database configuration
type Config struct {
Backend string // "sqlite" or "postgres"
// SQLite config
Path string // database file path
// PostgreSQL config
Host string
Port int
Database string
User string
Password string
SSLMode string
}