fix(autoimport): enable cold-start bootstrap for read-only commands (#977)

After devcontainer restart (cold-start), `bd --no-daemon show` failed to
find beads because:
1. Read-only commands skipped auto-import
2. Newly created DB had no issue_prefix set, causing import to fail

This fix enables seamless cold-start recovery by:
- Allowing read-only commands (show, list, etc.) to auto-bootstrap when
  JSONL exists but DB doesn't
- Setting needsBootstrap flag when falling back from read-only to
  read-write mode for missing DB
- Auto-detecting and setting issue_prefix from JSONL during auto-import
  when DB is uninitialized

Fixes: gt-b09

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Josh Nichols
2026-01-09 15:38:18 -05:00
committed by GitHub
parent edbfd5dc96
commit 5d1a8c2428
3 changed files with 96 additions and 25 deletions

View File

@@ -442,7 +442,19 @@ var rootCmd = &cobra.Command{
isYamlOnlyConfigOp = true
}
}
if cmd.Name() != "import" && cmd.Name() != "setup" && !isYamlOnlyConfigOp {
// Allow read-only commands to auto-bootstrap from JSONL (GH#b09)
// This enables `bd --no-daemon show` after cold-start when DB is missing
canAutoBootstrap := false
if isReadOnlyCommand(cmd.Name()) && beadsDir != "" {
jsonlPath := filepath.Join(beadsDir, "issues.jsonl")
if _, err := os.Stat(jsonlPath); err == nil {
canAutoBootstrap = true
debug.Logf("cold-start bootstrap: JSONL exists, allowing auto-create for %s", cmd.Name())
}
}
if cmd.Name() != "import" && cmd.Name() != "setup" && !isYamlOnlyConfigOp && !canAutoBootstrap {
// No database found - provide context-aware error message
fmt.Fprintf(os.Stderr, "Error: no beads database found\n")
@@ -707,6 +719,7 @@ var rootCmd = &cobra.Command{
// Fall back to direct storage access
var err error
var needsBootstrap bool // Track if DB needs initial import (GH#b09)
if useReadOnly {
// Read-only mode: prevents file modifications (GH#804)
store, err = sqlite.NewReadOnlyWithTimeout(rootCtx, dbPath, lockTimeout)
@@ -715,6 +728,7 @@ var rootCmd = &cobra.Command{
// This handles the case where user runs "bd list" before "bd init"
debug.Logf("read-only open failed, falling back to read-write: %v", err)
store, err = sqlite.NewWithTimeout(rootCtx, dbPath, lockTimeout)
needsBootstrap = true // New DB needs auto-import (GH#b09)
}
} else {
store, err = sqlite.NewWithTimeout(rootCtx, dbPath, lockTimeout)
@@ -760,7 +774,9 @@ var rootCmd = &cobra.Command{
// Skip for delete command to prevent resurrection of deleted issues
// Skip if sync --dry-run to avoid modifying DB in dry-run mode
// Skip for read-only commands - they can't write anyway (GH#804)
if cmd.Name() != "import" && cmd.Name() != "delete" && autoImportEnabled && !useReadOnly {
// Exception: allow auto-import for read-only commands that fell back to
// read-write mode due to missing DB (needsBootstrap) - fixes GH#b09
if cmd.Name() != "import" && cmd.Name() != "delete" && autoImportEnabled && (!useReadOnly || needsBootstrap) {
// Check if this is sync command with --dry-run flag
if cmd.Name() == "sync" {
if dryRun, _ := cmd.Flags().GetBool("dry-run"); dryRun {