fix(daemon): normalize paths for case-insensitive filesystem comparison (GH#869)

On macOS and Windows, filesystems are typically case-insensitive, so
/Users/foo/Desktop and /Users/foo/desktop refer to the same directory.
The daemon registry and discovery code was doing direct string comparison,
causing path mismatches when the casing differed.

Fix:
- Add NormalizePathForComparison() and PathsEqual() to internal/utils/path.go
- These resolve symlinks and lowercase paths on darwin/windows
- Update all workspace path comparisons in registry.go, discovery.go, and
  daemons.go to use PathsEqual()

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
wolf
2026-01-03 13:22:52 -08:00
committed by Steve Yegge
parent a791991103
commit 631b067c1c
5 changed files with 194 additions and 6 deletions

View File

@@ -10,6 +10,7 @@ import (
"github.com/steveyegge/beads/internal/lockfile"
"github.com/steveyegge/beads/internal/rpc"
"github.com/steveyegge/beads/internal/utils"
)
// walkWithDepth walks a directory tree with depth limiting
@@ -229,7 +230,8 @@ func FindDaemonByWorkspace(workspacePath string) (*DaemonInfo, error) {
}
for _, daemon := range daemons {
if daemon.WorkspacePath == workspacePath && daemon.Alive {
// Use PathsEqual for case-insensitive comparison on macOS/Windows (GH#869)
if utils.PathsEqual(daemon.WorkspacePath, workspacePath) && daemon.Alive {
return &daemon, nil
}
}

View File

@@ -9,6 +9,7 @@ import (
"time"
"github.com/steveyegge/beads/internal/lockfile"
"github.com/steveyegge/beads/internal/utils"
)
// RegistryEntry represents a daemon entry in the registry
@@ -179,9 +180,10 @@ func (r *Registry) Register(entry RegistryEntry) error {
}
// Remove any existing entry for this workspace or PID
// Use PathsEqual for case-insensitive comparison on macOS/Windows (GH#869)
filtered := []RegistryEntry{}
for _, e := range entries {
if e.WorkspacePath != entry.WorkspacePath && e.PID != entry.PID {
if !utils.PathsEqual(e.WorkspacePath, entry.WorkspacePath) && e.PID != entry.PID {
filtered = append(filtered, e)
}
}
@@ -202,9 +204,10 @@ func (r *Registry) Unregister(workspacePath string, pid int) error {
}
// Filter out entries matching workspace or PID
// Use PathsEqual for case-insensitive comparison on macOS/Windows (GH#869)
filtered := []RegistryEntry{}
for _, e := range entries {
if e.WorkspacePath != workspacePath && e.PID != pid {
if !utils.PathsEqual(e.WorkspacePath, workspacePath) && e.PID != pid {
filtered = append(filtered, e)
}
}