refactor(doctor): consolidate maintenance commands + improve daemon startup

Consolidate clean, repair-deps, validate into bd doctor:
- Add validation checks to doctor (merge artifacts, orphaned deps, duplicates, test pollution, git conflicts)
- Add auto-fix for merge artifacts and orphaned dependencies
- Delete obsolete command files: clean.go, repair_deps.go, validate.go
- Delete orphaned test files: clean_security_test.go, validate_test.go

Improve daemon startup performance:
- Add fast-fail detection when daemon crashed (check lock before retrying)
- Reduce graceful shutdown timeout from 5s to 1s
- Skip daemon connection for root command (just shows help)
- Extract shutdown timeout as constants (daemonShutdownTimeout, daemonShutdownPollInterval)

Other changes:
- Move rename-prefix command to Maintenance group in help
- Fix Makefile to inject git commit hash via ldflags

New files:
- cmd/bd/doctor/validation.go (5 check functions)
- cmd/bd/doctor/fix/validation.go (2 fix functions)
This commit is contained in:
Ryan Snodgrass
2025-12-22 19:39:51 -08:00
committed by Steve Yegge
parent e60dfaf1f1
commit cafc0b9dfb
16 changed files with 619 additions and 1374 deletions

View File

@@ -15,6 +15,16 @@ import (
"github.com/steveyegge/beads/internal/ui"
)
// daemonShutdownTimeout is how long to wait for graceful shutdown before force killing.
// 1 second is sufficient - if daemon hasn't stopped by then, it's likely hung.
const daemonShutdownTimeout = 1 * time.Second
// daemonShutdownPollInterval is how often to check if daemon has stopped.
const daemonShutdownPollInterval = 100 * time.Millisecond
// daemonShutdownAttempts is the number of poll attempts before force kill.
const daemonShutdownAttempts = int(daemonShutdownTimeout / daemonShutdownPollInterval)
// Daemon start failure tracking for exponential backoff
var (
lastDaemonStartAttempt time.Time
@@ -72,9 +82,9 @@ func restartDaemonForVersionMismatch() bool {
return false
}
// Wait for daemon to stop (up to 5 seconds)
for i := 0; i < 50; i++ {
time.Sleep(100 * time.Millisecond)
// Wait for daemon to stop, then force kill
for i := 0; i < daemonShutdownAttempts; i++ {
time.Sleep(daemonShutdownPollInterval)
if isRunning, _ := isDaemonRunning(pidFile); !isRunning {
debug.Logf("old daemon stopped successfully")
break