fix(autoimport): auto-correct deleted status to tombstone for JSONL compatibility (#1231)
* fix(autoimport): auto-correct deleted status to tombstone for JSONL compatibility (GH#1223) This fix addresses the 'Stuck in sync diversion loop' issue where v0.48.0 encountered validation errors during JSONL import. The issue occurs when JSONL files from older versions have issues with status='deleted' but the current code expects status='tombstone' for deleted issues. Changes: - Add migration logic in parseJSONL to auto-correct 'deleted' status to 'tombstone' - Ensure tombstones always have deleted_at timestamp set - Add debug logging for both migration operations - Prevents users from being stuck in sync divergence when upgrading Fixes GH#1223: Stuck in sync diversion loop * fix(autoimport): comprehensively fix corrupted deleted_at on non-tombstone issues (GH#1223) The initial fix for GH#1223 only caught issues with status='deleted', but the real data in the wild had issues with status='closed' (or other statuses) but also had deleted_at set, which violates the validation rule. Changes: - Add broader migration logic: any non-tombstone issue with deleted_at should become tombstone - Apply fix in all three JSONL parsing locations: - internal/autoimport/autoimport.go (parseJSONL for auto-import) - cmd/bd/import.go (import command) - cmd/bd/daemon_sync.go (daemon sync helper) - Add comprehensive test case for corrupted closed issues with deleted_at - Fixes the 'non-tombstone issues cannot have deleted_at timestamp' validation error during fresh bd init or import Fixes GH#1223: Stuck in sync diversion loop * Add merge driver comment to .gitattributes * fix: properly clean up .gitattributes during bd admin reset Fixes GH#1223 - Stuck in sync diversion loop The removeGitattributesEntry() function was not properly cleaning up beads-related entries from .gitattributes. It only removed lines containing "merge=beads" but left behind: - The comment line "# Use bd merge for beads JSONL files" - Empty lines following removed entries This caused .gitattributes to remain in a modified state after bd admin reset --force, triggering sync divergence warning loop. The fix now: - Skips lines containing "merge=beads" (existing behavior) - Skips beads-related comment lines - Skips empty lines that follow removed beads entries - Properly cleans up file so it's either empty (and gets deleted) or contains only non-beads content --------- Co-authored-by: Amp <amp@example.com>
This commit is contained in:
@@ -187,6 +187,30 @@ func importToJSONLWithStore(ctx context.Context, store storage.Storage, jsonlPat
|
||||
}
|
||||
issue.SetDefaults() // Apply defaults for omitted fields
|
||||
|
||||
// Migrate old JSONL format: auto-correct deleted status to tombstone
|
||||
// This handles JSONL files from versions that used "deleted" instead of "tombstone"
|
||||
// (GH#1223: Stuck in sync diversion loop)
|
||||
if issue.Status == types.Status("deleted") && issue.DeletedAt != nil {
|
||||
issue.Status = types.StatusTombstone
|
||||
}
|
||||
|
||||
// Fix: Any non-tombstone issue with deleted_at set is malformed and should be tombstone
|
||||
// This catches issues that may have been corrupted or migrated incorrectly
|
||||
if issue.Status != types.StatusTombstone && issue.DeletedAt != nil {
|
||||
issue.Status = types.StatusTombstone
|
||||
}
|
||||
|
||||
if issue.Status == types.StatusClosed && issue.ClosedAt == nil {
|
||||
now := time.Now()
|
||||
issue.ClosedAt = &now
|
||||
}
|
||||
|
||||
// Ensure tombstones have deleted_at set (fix for malformed data)
|
||||
if issue.Status == types.StatusTombstone && issue.DeletedAt == nil {
|
||||
now := time.Now()
|
||||
issue.DeletedAt = &now
|
||||
}
|
||||
|
||||
issues = append(issues, &issue)
|
||||
}
|
||||
|
||||
|
||||
@@ -212,6 +212,39 @@ NOTE: Import requires direct database access and does not work with daemon mode.
|
||||
}
|
||||
issue.SetDefaults() // Apply defaults for omitted fields (beads-399)
|
||||
|
||||
// Migrate old JSONL format: auto-correct deleted status to tombstone
|
||||
// This handles JSONL files from versions that used "deleted" instead of "tombstone"
|
||||
// (GH#1223: Stuck in sync diversion loop)
|
||||
if issue.Status == types.Status("deleted") && issue.DeletedAt != nil {
|
||||
issue.Status = types.StatusTombstone
|
||||
if debug.Enabled() {
|
||||
debug.Logf("Auto-corrected status 'deleted' to 'tombstone' for issue %s\n", issue.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Fix: Any non-tombstone issue with deleted_at set is malformed and should be tombstone
|
||||
// This catches issues that may have been corrupted or migrated incorrectly
|
||||
if issue.Status != types.StatusTombstone && issue.DeletedAt != nil {
|
||||
issue.Status = types.StatusTombstone
|
||||
if debug.Enabled() {
|
||||
debug.Logf("Auto-corrected status %s to 'tombstone' (had deleted_at) for issue %s\n", issue.Status, issue.ID)
|
||||
}
|
||||
}
|
||||
|
||||
if issue.Status == types.StatusClosed && issue.ClosedAt == nil {
|
||||
now := time.Now()
|
||||
issue.ClosedAt = &now
|
||||
}
|
||||
|
||||
// Ensure tombstones have deleted_at set (fix for malformed data)
|
||||
if issue.Status == types.StatusTombstone && issue.DeletedAt == nil {
|
||||
now := time.Now()
|
||||
issue.DeletedAt = &now
|
||||
if debug.Enabled() {
|
||||
debug.Logf("Auto-added deleted_at timestamp for tombstone issue %s\n", issue.ID)
|
||||
}
|
||||
}
|
||||
|
||||
allIssues = append(allIssues, &issue)
|
||||
}
|
||||
|
||||
|
||||
@@ -344,10 +344,30 @@ func removeGitattributesEntry() error {
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
var newLines []string
|
||||
skipNextEmpty := false
|
||||
|
||||
for _, line := range lines {
|
||||
if !strings.Contains(line, "merge=beads") {
|
||||
newLines = append(newLines, line)
|
||||
// Skip lines containing beads merge configuration
|
||||
if strings.Contains(line, "merge=beads") {
|
||||
skipNextEmpty = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip beads-related comment lines
|
||||
if strings.Contains(line, "Use bd merge for beads JSONL files") {
|
||||
skipNextEmpty = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip empty lines that follow removed beads entries
|
||||
if skipNextEmpty && strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
skipNextEmpty = false
|
||||
|
||||
// Keep the line
|
||||
newLines = append(newLines, line)
|
||||
}
|
||||
|
||||
newContent := strings.Join(newLines, "\n")
|
||||
|
||||
Reference in New Issue
Block a user