fix: tombstone/deletion overhaul for bd-4q8
- IsExpired(): Negative TTL means immediately expired (for --hard mode) - IsExpired(): ClockSkewGrace only added for TTLs > 1 hour - bd cleanup --hard: Use negative TTL to prune freshly created tombstones - bd delete --hard: New flag to immediately prune tombstones from JSONL - Import: Add early tombstone check before all phases to prevent resurrection The early tombstone check prevents ghost issues from being created when tombstones exist in the DB. However, a deeper git merge issue (bd-ncwo) can still cause resurrection when remote's status:closed wins the merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -98,7 +98,10 @@ func (i *Issue) IsTombstone() bool {
|
||||
|
||||
// IsExpired returns true if the tombstone has exceeded its TTL.
|
||||
// Non-tombstone issues always return false.
|
||||
// ttl is the configured TTL duration; if zero, DefaultTombstoneTTL is used.
|
||||
// ttl is the configured TTL duration:
|
||||
// - If zero, DefaultTombstoneTTL (30 days) is used
|
||||
// - If negative, the tombstone is immediately expired (for --hard mode)
|
||||
// - If positive, ClockSkewGrace is added only for TTLs > 1 hour
|
||||
func (i *Issue) IsExpired(ttl time.Duration) bool {
|
||||
// Non-tombstones never expire
|
||||
if !i.IsTombstone() {
|
||||
@@ -110,13 +113,22 @@ func (i *Issue) IsExpired(ttl time.Duration) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Negative TTL means "immediately expired" - for --hard mode (bd-4q8 fix)
|
||||
if ttl < 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Use default TTL if not specified
|
||||
if ttl == 0 {
|
||||
ttl = DefaultTombstoneTTL
|
||||
}
|
||||
|
||||
// Add clock skew grace period to the TTL
|
||||
effectiveTTL := ttl + ClockSkewGrace
|
||||
// Only add clock skew grace period for normal TTLs (> 1 hour).
|
||||
// For short TTLs (testing/development), skip grace period.
|
||||
effectiveTTL := ttl
|
||||
if ttl > ClockSkewGrace {
|
||||
effectiveTTL = ttl + ClockSkewGrace
|
||||
}
|
||||
|
||||
// Check if the tombstone has exceeded its TTL
|
||||
expirationTime := i.DeletedAt.Add(effectiveTTL)
|
||||
|
||||
@@ -756,6 +756,31 @@ func TestIsExpired(t *testing.T) {
|
||||
ttl: 7 * 24 * time.Hour,
|
||||
expired: false,
|
||||
},
|
||||
{
|
||||
name: "negative TTL means immediately expired (bd-4q8 --hard mode)",
|
||||
issue: Issue{
|
||||
ID: "test-14",
|
||||
Title: "(deleted)",
|
||||
Status: StatusTombstone,
|
||||
Priority: 0,
|
||||
IssueType: TypeTask,
|
||||
DeletedAt: timePtr(now), // Just deleted NOW
|
||||
},
|
||||
ttl: -1, // Negative TTL = immediate expiration
|
||||
expired: true,
|
||||
},
|
||||
{
|
||||
name: "non-tombstone never expires even with negative TTL",
|
||||
issue: Issue{
|
||||
ID: "test-15",
|
||||
Title: "Open issue",
|
||||
Status: StatusOpen,
|
||||
Priority: 0,
|
||||
IssueType: TypeTask,
|
||||
},
|
||||
ttl: -1,
|
||||
expired: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user