feat(types): add StatusPinned for persistent beads (bd-6v2)

Add pinned status for beads that should stay open indefinitely:
- Add StatusPinned constant and update IsValid()
- Add PinnedIssues count to Statistics struct
- Protect pinned issues from bd close (requires --force)
- Show pinned count in bd stats output

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-18 21:55:27 -08:00
parent 8fa1eda207
commit f4f51da007
4 changed files with 39 additions and 3 deletions

View File

@@ -111,15 +111,17 @@ func (s *SQLiteStorage) GetStatistics(ctx context.Context) (*types.Statistics, e
var stats types.Statistics
// Get counts (bd-nyt: exclude tombstones from TotalIssues, report separately)
// (bd-6v2: also count pinned issues)
err := s.db.QueryRowContext(ctx, `
SELECT
COALESCE(SUM(CASE WHEN status != 'tombstone' THEN 1 ELSE 0 END), 0) as total,
COALESCE(SUM(CASE WHEN status = 'open' THEN 1 ELSE 0 END), 0) as open,
COALESCE(SUM(CASE WHEN status = 'in_progress' THEN 1 ELSE 0 END), 0) as in_progress,
COALESCE(SUM(CASE WHEN status = 'closed' THEN 1 ELSE 0 END), 0) as closed,
COALESCE(SUM(CASE WHEN status = 'tombstone' THEN 1 ELSE 0 END), 0) as tombstone
COALESCE(SUM(CASE WHEN status = 'tombstone' THEN 1 ELSE 0 END), 0) as tombstone,
COALESCE(SUM(CASE WHEN status = 'pinned' THEN 1 ELSE 0 END), 0) as pinned
FROM issues
`).Scan(&stats.TotalIssues, &stats.OpenIssues, &stats.InProgressIssues, &stats.ClosedIssues, &stats.TombstoneIssues)
`).Scan(&stats.TotalIssues, &stats.OpenIssues, &stats.InProgressIssues, &stats.ClosedIssues, &stats.TombstoneIssues, &stats.PinnedIssues)
if err != nil {
return nil, fmt.Errorf("failed to get issue counts: %w", err)
}

View File

@@ -186,12 +186,13 @@ const (
StatusBlocked Status = "blocked"
StatusClosed Status = "closed"
StatusTombstone Status = "tombstone" // Soft-deleted issue (bd-vw8)
StatusPinned Status = "pinned" // Persistent bead that stays open indefinitely (bd-6v2)
)
// IsValid checks if the status value is valid (built-in statuses only)
func (s Status) IsValid() bool {
switch s {
case StatusOpen, StatusInProgress, StatusBlocked, StatusClosed, StatusTombstone:
case StatusOpen, StatusInProgress, StatusBlocked, StatusClosed, StatusTombstone, StatusPinned:
return true
}
return false
@@ -390,6 +391,7 @@ type Statistics struct {
BlockedIssues int `json:"blocked_issues"`
ReadyIssues int `json:"ready_issues"`
TombstoneIssues int `json:"tombstone_issues"` // Soft-deleted issues (bd-nyt)
PinnedIssues int `json:"pinned_issues"` // Persistent issues (bd-6v2)
EpicsEligibleForClosure int `json:"epics_eligible_for_closure"`
AverageLeadTime float64 `json:"average_lead_time_hours"`
}