Fix critical double-release race in importInProgress flag

CRITICAL BUG: The previous fix had a race condition where the
importInProgress flag could be released twice, allowing two goroutines
to think they both hold the lock.

Bug scenario:
1. Goroutine A: acquires lock (CAS true)
2. Goroutine A: manually releases at line 208 for git dirty skip
3. Goroutine B: CAS succeeds, acquires lock
4. Goroutine A: defer runs, releases flag AGAIN (clears B lock)
5. Goroutine C: CAS succeeds - now TWO goroutines have lock

Root cause: Using both manual Store(false) AND defer Store(false)
created a window where the flag could be cleared twice.

Fix: Use a shouldDeferRelease flag to disable the deferred release
when we manually release early. This ensures exactly one release
per acquisition.

Testing: All auto-import tests still passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-11-02 18:16:25 -08:00
parent e8e9e729e5
commit 3b6856904f
2 changed files with 13 additions and 2 deletions

2
.beads/daemon-stderr.log Normal file
View File

@@ -0,0 +1,2 @@
Error: daemon already running (PID 27867)
Use 'bd daemon --stop' to stop it first

View File

@@ -196,7 +196,15 @@ func (s *Server) checkAndAutoImportIfStale(req *Request) error {
}
return nil
}
defer s.importInProgress.Store(false)
// Track whether we should release the lock via defer
// Set to false if we manually release early to avoid double-release bug
shouldDeferRelease := true
defer func() {
if shouldDeferRelease {
s.importInProgress.Store(false)
}
}()
// Check if git has uncommitted changes that include beads files (bd-8931)
// If JSONL files are uncommitted, skip auto-import to avoid conflicts
@@ -204,8 +212,9 @@ func (s *Server) checkAndAutoImportIfStale(req *Request) error {
dbDir := filepath.Dir(dbPath)
workspaceRoot := filepath.Dir(dbDir) // Go up from .beads to workspace root
if hasUncommittedBeadsFiles(workspaceRoot) {
// CRITICAL: Must release lock before returning to avoid race condition
// CRITICAL: Release lock and disable defer to avoid double-release race
s.importInProgress.Store(false)
shouldDeferRelease = false
if os.Getenv("BD_DEBUG") != "" {
fmt.Fprintf(os.Stderr, "Debug: skipping auto-import, .beads files have uncommitted changes\n")