feat(cleanup): protect pinned issues from cleanup/compact (bd-b2k)

Add Pinned field to Issue struct and database schema to protect
issues from accidental deletion via cleanup or compaction.

Changes:
- Add Pinned bool field to types.Issue
- Create migration 023_pinned_column.go for database schema
- Filter out pinned issues in cleanup command before deletion
- Add pinned check to GetTier1Candidates and GetTier2Candidates
- Add pinned check to CheckEligibility for compaction
- Update all SQL queries and scan functions to include pinned field

🤖 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-19 00:42:35 -08:00
parent 9dc34da64a
commit f032eccbad
13 changed files with 152 additions and 35 deletions

View File

@@ -101,7 +101,7 @@ func (s *SQLiteStorage) GetReadyWork(ctx context.Context, filter types.WorkFilte
i.status, i.priority, i.issue_type, i.assignee, i.estimated_minutes,
i.created_at, i.updated_at, i.closed_at, i.external_ref, i.source_repo, i.close_reason,
i.deleted_at, i.deleted_by, i.delete_reason, i.original_type,
i.sender, i.ephemeral
i.sender, i.ephemeral, i.pinned
FROM issues i
WHERE %s
AND NOT EXISTS (
@@ -130,7 +130,7 @@ func (s *SQLiteStorage) GetStaleIssues(ctx context.Context, filter types.StaleFi
created_at, updated_at, closed_at, external_ref, source_repo,
compaction_level, compacted_at, compacted_at_commit, original_size, close_reason,
deleted_at, deleted_by, delete_reason, original_type,
sender, ephemeral
sender, ephemeral, pinned
FROM issues
WHERE status != 'closed'
AND datetime(updated_at) < datetime('now', '-' || ? || ' days')
@@ -179,6 +179,8 @@ func (s *SQLiteStorage) GetStaleIssues(ctx context.Context, filter types.StaleFi
// Messaging fields (bd-kwro)
var sender sql.NullString
var ephemeral sql.NullInt64
// Protection fields (bd-b2k)
var pinned sql.NullInt64
err := rows.Scan(
&issue.ID, &contentHash, &issue.Title, &issue.Description, &issue.Design,
@@ -187,7 +189,7 @@ func (s *SQLiteStorage) GetStaleIssues(ctx context.Context, filter types.StaleFi
&issue.CreatedAt, &issue.UpdatedAt, &closedAt, &externalRef, &sourceRepo,
&compactionLevel, &compactedAt, &compactedAtCommit, &originalSize, &closeReason,
&deletedAt, &deletedBy, &deleteReason, &originalType,
&sender, &ephemeral,
&sender, &ephemeral, &pinned,
)
if err != nil {
return nil, fmt.Errorf("failed to scan stale issue: %w", err)
@@ -244,6 +246,10 @@ func (s *SQLiteStorage) GetStaleIssues(ctx context.Context, filter types.StaleFi
if ephemeral.Valid && ephemeral.Int64 != 0 {
issue.Ephemeral = true
}
// Protection fields (bd-b2k)
if pinned.Valid && pinned.Int64 != 0 {
issue.Pinned = true
}
issues = append(issues, &issue)
}