From 85ac1e126be81385d688b42a933a44f1d1e8df64 Mon Sep 17 00:00:00 2001 From: beads/crew/grip Date: Sun, 4 Jan 2026 16:24:05 -0800 Subject: [PATCH] fix(nodb): set cmdCtx.StoreActive in initializeNoDbMode (GH#897) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bug: initializeNoDbMode() was setting the legacy global storeActive but not cmdCtx.StoreActive. When ensureStoreActive() checked isStoreActive(), it used cmdCtx.StoreActive (which was false), causing the JSONL-only mode error even when --no-db was passed. The fix: Use accessor functions (lockStore, setStore, setStoreActive, unlockStore) which set both the legacy globals and cmdCtx fields. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- cmd/bd/nodb.go | 9 ++++--- cmd/bd/nodb_test.go | 65 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/cmd/bd/nodb.go b/cmd/bd/nodb.go index 1b40fb68..235e719f 100644 --- a/cmd/bd/nodb.go +++ b/cmd/bd/nodb.go @@ -73,10 +73,11 @@ func initializeNoDbMode() error { debug.Logf("using prefix '%s'", prefix) // Set global store and mark as active (fixes bd comment --no-db) - storeMutex.Lock() - store = memStore - storeActive = true - storeMutex.Unlock() + // GH#897: Use accessor functions to also set cmdCtx fields, not just globals + lockStore() + setStore(memStore) + setStoreActive(true) + unlockStore() return nil } diff --git a/cmd/bd/nodb_test.go b/cmd/bd/nodb_test.go index 83208bef..51aa4ec7 100644 --- a/cmd/bd/nodb_test.go +++ b/cmd/bd/nodb_test.go @@ -301,6 +301,71 @@ func TestInitializeNoDbMode_SetsStoreActive(t *testing.T) { } } +func TestInitializeNoDbMode_SetsCmdCtxStoreActive(t *testing.T) { + // GH#897: Verify that initializeNoDbMode sets cmdCtx.StoreActive, not just the global. + // This is critical for commands like `comments add` that call ensureStoreActive(). + ensureCleanGlobalState(t) + + tempDir := t.TempDir() + beadsDir := filepath.Join(tempDir, ".beads") + if err := os.MkdirAll(beadsDir, 0o755); err != nil { + t.Fatalf("Failed to create .beads dir: %v", err) + } + + // Create a minimal JSONL file with one issue + jsonlPath := filepath.Join(beadsDir, "issues.jsonl") + content := `{"id":"mmm-155","title":"Test Issue","status":"open"} +` + if err := os.WriteFile(jsonlPath, []byte(content), 0o600); err != nil { + t.Fatalf("Failed to write JSONL: %v", err) + } + + // Initialize CommandContext (simulates what PersistentPreRun does) + initCommandContext() + + oldCwd, _ := os.Getwd() + defer func() { + _ = os.Chdir(oldCwd) + resetCommandContext() + }() + + // Change to temp dir so initializeNoDbMode finds .beads + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("Failed to chdir: %v", err) + } + + // Initialize no-db mode + if err := initializeNoDbMode(); err != nil { + t.Fatalf("initializeNoDbMode failed: %v", err) + } + + // Verify cmdCtx.StoreActive is true (this was the bug - it was only setting globals) + ctx := GetCommandContext() + if ctx == nil { + t.Fatal("cmdCtx should not be nil after initCommandContext") + } + if !ctx.StoreActive { + t.Error("cmdCtx.StoreActive should be true after initializeNoDbMode (GH#897)") + } + if ctx.Store == nil { + t.Error("cmdCtx.Store should not be nil after initializeNoDbMode") + } + + // ensureStoreActive should succeed + if err := ensureStoreActive(); err != nil { + t.Errorf("ensureStoreActive should succeed after initializeNoDbMode: %v", err) + } + + // Comments should work + comment, err := ctx.Store.AddIssueComment(rootCtx, "mmm-155", "testuser", "Test comment") + if err != nil { + t.Fatalf("AddIssueComment failed: %v", err) + } + if comment.Text != "Test comment" { + t.Errorf("Expected 'Test comment', got %s", comment.Text) + } +} + func TestWriteIssuesToJSONL(t *testing.T) { tempDir := t.TempDir() beadsDir := filepath.Join(tempDir, ".beads")