diff --git a/internal/storage/sqlite/validators.go b/internal/storage/sqlite/validators.go index bbcc1998..dd1571cc 100644 --- a/internal/storage/sqlite/validators.go +++ b/internal/storage/sqlite/validators.go @@ -22,8 +22,14 @@ func validateStatus(value interface{}) error { } // validateStatusWithCustom validates a status value, allowing custom statuses. +// Note: tombstone status is blocked here (bd-y68) - use bd delete instead of bd update --status=tombstone func validateStatusWithCustom(value interface{}, customStatuses []string) error { if status, ok := value.(string); ok { + // Block direct status update to tombstone (bd-y68) + // Tombstones should only be created via bd delete, not bd update --status=tombstone + if types.Status(status) == types.StatusTombstone { + return fmt.Errorf("cannot set status to tombstone directly; use 'bd delete' instead") + } if !types.Status(status).IsValidWithCustom(customStatuses) { return fmt.Errorf("invalid status: %s", status) } diff --git a/internal/types/types.go b/internal/types/types.go index 86f9a2bc..fdb5e04d 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -112,6 +112,13 @@ func (i *Issue) ValidateWithCustomStatuses(customStatuses []string) error { if i.Status != StatusClosed && i.ClosedAt != nil { return fmt.Errorf("non-closed issues cannot have closed_at timestamp") } + // Enforce tombstone invariants (bd-md2): deleted_at must be set for tombstones, and only for tombstones + if i.Status == StatusTombstone && i.DeletedAt == nil { + return fmt.Errorf("tombstone issues must have deleted_at timestamp") + } + if i.Status != StatusTombstone && i.DeletedAt != nil { + return fmt.Errorf("non-tombstone issues cannot have deleted_at timestamp") + } return nil }