Fix critical P0 database reinitialization bug (bd-130)
Fixes silent data loss when .beads/ directory removed and daemon auto-starts. Root cause: checkGitForIssues() hardcoded 'issues.jsonl' but git tracks 'beads.jsonl' Changes: - Fix A (bd-131): checkGitForIssues() tries beads.jsonl first, then issues.jsonl - Fix B (bd-132): Immediate export after import in bd init to prevent daemon race - Fix C (bd-133): Safety check that fails loudly if import fails - Fix D (bd-134): Daemon startup auto-import when DB empty but git has issues - Tests (bd-135): Comprehensive integration test suite Oracle-recommended improvements: - Export to exact git-relative path (prevents path drift) - filepath.ToSlash for Windows git compatibility - 64MB scanner buffer for large JSONL lines - Improved safety check messages (only suggest local file if exists) All tests passing. No regressions. Amp-Thread-ID: https://ampcode.com/threads/T-0e31dc6a-a0d9-46c6-87b2-cfdebe829a52 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -145,11 +145,56 @@ bd.db
|
||||
fmt.Fprintf(os.Stderr, "Try manually: git show HEAD:%s | bd import -i /dev/stdin\n", jsonlPath)
|
||||
}
|
||||
// Non-fatal - continue with empty database
|
||||
} else if !quiet {
|
||||
fmt.Fprintf(os.Stderr, "✓ Successfully imported %d issues from git.\n\n", issueCount)
|
||||
} else {
|
||||
// CRITICAL: Immediately export to local JSONL to prevent daemon race condition
|
||||
// The daemon might auto-start before the 5-second auto-flush debounce completes
|
||||
// Write to exact git-relative path to prevent path drift
|
||||
gitRoot := findGitRoot()
|
||||
if gitRoot == "" {
|
||||
if !quiet {
|
||||
fmt.Fprintf(os.Stderr, "Warning: could not find git root for export\n")
|
||||
}
|
||||
} else {
|
||||
absJSONL := filepath.Join(gitRoot, filepath.FromSlash(jsonlPath))
|
||||
if err := os.MkdirAll(filepath.Dir(absJSONL), 0750); err != nil {
|
||||
if !quiet {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to create export dir: %v\n", err)
|
||||
}
|
||||
} else if err := exportToJSONLWithStore(ctx, store, absJSONL); err != nil {
|
||||
if !quiet {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to export after import: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !quiet {
|
||||
fmt.Fprintf(os.Stderr, "✓ Successfully imported %d issues from git.\n\n", issueCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safety check: verify import succeeded and catch silent data loss
|
||||
stats, err := store.GetStatistics(ctx)
|
||||
if err == nil && stats.TotalIssues == 0 {
|
||||
// DB empty after init - check if git has issues we failed to import
|
||||
recheck, recheckPath := checkGitForIssues()
|
||||
if recheck > 0 {
|
||||
fmt.Fprintf(os.Stderr, "\n❌ ERROR: Database empty but git has %d issues!\n", recheck)
|
||||
fmt.Fprintf(os.Stderr, "Auto-import failed. Manual recovery:\n")
|
||||
fmt.Fprintf(os.Stderr, " git show HEAD:%s | bd import -i /dev/stdin\n", filepath.ToSlash(recheckPath))
|
||||
// Only suggest local file import if file exists
|
||||
gitRoot := findGitRoot()
|
||||
if gitRoot != "" {
|
||||
localFile := filepath.Join(gitRoot, filepath.FromSlash(recheckPath))
|
||||
if _, err := os.Stat(localFile); err == nil {
|
||||
fmt.Fprintf(os.Stderr, "Or:\n")
|
||||
fmt.Fprintf(os.Stderr, " bd import -i %s\n", localFile)
|
||||
}
|
||||
}
|
||||
_ = store.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := store.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to close database: %v\n", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user