Add client self-heal for stale daemon.pid

- When socket missing and lock free, automatically remove stale daemon.pid
- Prevents stale artifacts from accumulating after daemon crashes
- Includes comprehensive test coverage
- Fixes bd-1mzt

Amp-Thread-ID: https://ampcode.com/threads/T-3f606a8a-d591-4412-b994-ea790889a04d
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Steve Yegge
2025-11-07 21:21:24 -08:00
parent 00f045a972
commit a236558a7a
2 changed files with 124 additions and 0 deletions

View File

@@ -44,6 +44,8 @@ func TryConnectWithTimeout(socketPath string, dialTimeout time.Duration) (*Clien
running, _ := lockfile.TryDaemonLock(beadsDir)
if !running {
debug.Logf("daemon lock not held and socket missing (no daemon running)")
// Self-heal: clean up stale artifacts when lock is free and socket is missing
cleanupStaleDaemonArtifacts(beadsDir)
return nil, nil
}
}
@@ -333,3 +335,24 @@ func (c *Client) Export(args *ExportArgs) (*Response, error) {
func (c *Client) EpicStatus(args *EpicStatusArgs) (*Response, error) {
return c.Execute(OpEpicStatus, args)
}
// cleanupStaleDaemonArtifacts removes stale daemon.pid file when socket is missing and lock is free.
// This prevents stale artifacts from accumulating after daemon crashes.
// Only removes pid file - lock file is managed by OS (released on process exit).
func cleanupStaleDaemonArtifacts(beadsDir string) {
pidFile := filepath.Join(beadsDir, "daemon.pid")
// Check if pid file exists
if _, err := os.Stat(pidFile); err != nil {
// No pid file to clean up
return
}
// Remove stale pid file
if err := os.Remove(pidFile); err != nil {
debug.Logf("failed to remove stale pid file: %v", err)
return
}
debug.Logf("removed stale daemon.pid file (lock free, socket missing)")
}