From 46862568f6f8f92743ec065ffb55898bb154ed8e Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Fri, 28 Nov 2025 21:59:59 -0800 Subject: [PATCH] bd-4ew: Doctor detects fresh clone and recommends 'bd init' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When JSONL exists but no database (and not no-db mode), doctor now: - Shows 'Fresh clone detected' warning - Shows count of issues in JSONL - Recommends 'bd init' with detected prefix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .beads/issues.jsonl | 2 +- cmd/bd/doctor.go | 76 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 7c7fd9f7..4e6206a7 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -8,7 +8,7 @@ {"id":"bd-3gc","title":"Audit remaining cmd/bd files for error handling consistency","description":"Extend ERROR_HANDLING_AUDIT.md to cover: daemon_sync.go, update.go, list.go, show.go, close.go, reopen.go, dep.go, label.go, comments.go, delete.go, compact.go, config.go, validate.go and other high-usage command files","status":"open","priority":3,"issue_type":"task","created_at":"2025-11-24T00:28:55.890991-08:00","updated_at":"2025-11-24T00:28:55.890991-08:00"} {"id":"bd-44e","title":"Ensure deletions.jsonl is tracked in git","description":"Parent: bd-imj\n\nEnsure deletions.jsonl is tracked in git (not ignored).\n\nUpdate bd init and gitignore upgrade logic to:\n1. NOT add deletions.jsonl to .gitignore\n2. Ensure it is committed alongside beads.jsonl\n\nThe file must be in git for cross-clone propagation to work.\n\nAcceptance criteria:\n- bd init does not ignore deletions.jsonl\n- Existing .gitignore files are not broken\n- File appears in git status when modified","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-25T09:57:21.663196-08:00","updated_at":"2025-11-25T14:55:43.225883-08:00","closed_at":"2025-11-25T14:55:43.225883-08:00"} {"id":"bd-4aao","title":"Fix failing integration tests in beads-mcp","description":"The `beads-mcp` test suite has failures in `tests/test_bd_client_integration.py` (assertion error in `test_init_creates_beads_directory`) and errors in `tests/test_worktree_separate_dbs.py` (setup failures finding database). These need to be investigated and fixed to ensure a reliable CI baseline.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-20T18:53:28.4803-05:00","updated_at":"2025-11-25T21:39:20.967106-08:00","closed_at":"2025-11-25T21:39:20.967106-08:00"} -{"id":"bd-4ew","title":"bd doctor should detect fresh clone and recommend 'bd init'","description":"When running `bd doctor` on a fresh clone (JSONL exists, no .db file), it should:\n\n1. Detect this is a fresh clone situation\n2. Recommend `bd init --prefix \u003cdetected-prefix\u003e` as the fix\n3. Show the prefix detected from the JSONL file\n\nCurrently it shows various warnings (git hooks, merge driver, etc.) but doesn't address the fundamental issue: the database needs to be hydrated.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-27T20:21:15.691764-08:00","updated_at":"2025-11-27T20:21:15.691764-08:00"} +{"id":"bd-4ew","title":"bd doctor should detect fresh clone and recommend 'bd init'","description":"When running `bd doctor` on a fresh clone (JSONL exists, no .db file), it should:\n\n1. Detect this is a fresh clone situation\n2. Recommend `bd init --prefix \u003cdetected-prefix\u003e` as the fix\n3. Show the prefix detected from the JSONL file\n\nCurrently it shows various warnings (git hooks, merge driver, etc.) but doesn't address the fundamental issue: the database needs to be hydrated.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-27T20:21:15.691764-08:00","updated_at":"2025-11-28T21:59:51.036375-08:00","closed_at":"2025-11-28T21:59:51.036375-08:00"} {"id":"bd-4h3","title":"Add test coverage for internal/git package","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-20T21:21:23.497486-05:00","updated_at":"2025-11-28T21:57:22.201056-08:00","closed_at":"2025-11-28T21:55:45.2527-08:00","dependencies":[{"issue_id":"bd-4h3","depends_on_id":"bd-ge7","type":"blocks","created_at":"2025-11-20T21:21:31.277639-05:00","created_by":"daemon"}]} {"id":"bd-4l5","title":"bd prime: Detect ephemeral branches and adjust workflow output","description":"When 'bd prime' runs on a branch with no upstream (ephemeral branch), it should output a different SESSION CLOSE PROTOCOL.\n\n**Current output (wrong for ephemeral branches):**\n```\n[ ] 1. git status\n[ ] 2. git add \u003cfiles\u003e\n[ ] 3. bd sync\n[ ] 4. git commit -m \"...\"\n[ ] 5. bd sync\n[ ] 6. git push\n```\n\n**Needed output for ephemeral branches:**\n```\n[ ] 1. git status\n[ ] 2. git add \u003cfiles\u003e\n[ ] 3. bd sync --from-main (pull updates from main)\n[ ] 4. git commit -m \"...\"\n[ ] 5. (no push - branch is ephemeral)\n```\n\n**Detection:** `git rev-parse --abbrev-ref --symbolic-full-name @{u}` returns error code 128 if no upstream.\n\nAlso update Sync \u0026 Collaboration section to mention `bd sync --from-main` for ephemeral branches.\n\n**Use case:** Gastown polecats work on ephemeral local branches that are never pushed. Their code gets merged to main via local merge, and beads changes stay local (communicated via gm mail to Overseer).","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-11-25T16:55:24.984104-08:00","updated_at":"2025-11-25T17:12:46.604978-08:00","closed_at":"2025-11-25T17:12:46.604978-08:00"} {"id":"bd-4pv","title":"bd export only outputs 1 issue after auto-import corrupts database","description":"When auto-import runs and purges issues (due to git history backfill bug), subsequent 'bd export' only exports 1 issue even though the database should have many.\n\nReproduction:\n1. Have issues.jsonl with 55 issues\n2. Auto-import triggers and purges all issues via git history backfill\n3. Run 'bd export' - only exports 1 issue (the last one created before corruption)\n\nThe database gets into an inconsistent state where most issues are purged but export doesn't realize this.\n\nWorkaround: Rebuild database from scratch with 'rm .beads/beads.db \u0026\u0026 bd init --prefix bd'","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-11-26T22:28:40.828866-08:00","updated_at":"2025-11-27T22:50:35.036227-08:00","closed_at":"2025-11-27T22:50:35.036227-08:00"} diff --git a/cmd/bd/doctor.go b/cmd/bd/doctor.go index 1e4afc67..ecc306c6 100644 --- a/cmd/bd/doctor.go +++ b/cmd/bd/doctor.go @@ -636,26 +636,55 @@ func checkDatabaseVersion(path string) doctorCheck { // Check if database file exists if _, err := os.Stat(dbPath); os.IsNotExist(err) { - // Check if JSONL exists (--no-db mode) + // Check if JSONL exists // Check canonical (issues.jsonl) first, then legacy (beads.jsonl) issuesJSONL := filepath.Join(beadsDir, "issues.jsonl") beadsJSONL := filepath.Join(beadsDir, "beads.jsonl") + var jsonlPath string if _, err := os.Stat(issuesJSONL); err == nil { - return doctorCheck{ - Name: "Database", - Status: statusOK, - Message: "JSONL-only mode", - Detail: "Using issues.jsonl (no SQLite database)", - } + jsonlPath = issuesJSONL + } else if _, err := os.Stat(beadsJSONL); err == nil { + jsonlPath = beadsJSONL } - if _, err := os.Stat(beadsJSONL); err == nil { + if jsonlPath != "" { + // JSONL exists but no database - check if this is no-db mode or fresh clone + // Check config.yaml for no-db: true + configPath := filepath.Join(beadsDir, "config.yaml") + isNoDbMode := false + if configData, err := os.ReadFile(configPath); err == nil { + // Simple check for no-db: true in config.yaml + isNoDbMode = strings.Contains(string(configData), "no-db: true") + } + + if isNoDbMode { + return doctorCheck{ + Name: "Database", + Status: statusOK, + Message: "JSONL-only mode", + Detail: "Using issues.jsonl (no SQLite database)", + } + } + + // This is a fresh clone - JSONL exists but no database and not no-db mode + // Count issues and detect prefix for helpful suggestion + issueCount := countIssuesInJSONLFile(jsonlPath) + prefix := detectPrefixFromJSONL(jsonlPath) + + message := "Fresh clone detected (no database)" + detail := fmt.Sprintf("Found %d issue(s) in JSONL that need to be imported", issueCount) + fix := "Run 'bd init' to hydrate the database from JSONL" + if prefix != "" { + fix = fmt.Sprintf("Run 'bd init' to hydrate the database (detected prefix: %s)", prefix) + } + return doctorCheck{ Name: "Database", - Status: statusOK, - Message: "JSONL-only mode", - Detail: "Using issues.jsonl (no SQLite database)", + Status: statusWarning, + Message: message, + Detail: detail, + Fix: fix, } } @@ -2221,6 +2250,31 @@ func checkUntrackedBeadsFiles(path string) doctorCheck { } } +// countIssuesInJSONLFile counts the number of issues in a JSONL file +func countIssuesInJSONLFile(jsonlPath string) int { + count, _, _ := countJSONLIssues(jsonlPath) + return count +} + +// detectPrefixFromJSONL detects the most common issue prefix from a JSONL file +func detectPrefixFromJSONL(jsonlPath string) string { + _, prefixes, _ := countJSONLIssues(jsonlPath) + if len(prefixes) == 0 { + return "" + } + + // Find the most common prefix + var mostCommonPrefix string + maxCount := 0 + for prefix, count := range prefixes { + if count > maxCount { + maxCount = count + mostCommonPrefix = prefix + } + } + return mostCommonPrefix +} + func init() { rootCmd.AddCommand(doctorCmd) doctorCmd.Flags().BoolVar(&perfMode, "perf", false, "Run performance diagnostics and generate CPU profile")