Files
beads/cmd/bd/daemon_unix.go
Steve Yegge 58fe00057c feat: Complete GH #353 follow-up phases (bd-9nw, bd-u3t, bd-e0o)
Implements all three follow-up phases for sandbox environment support:

**Phase 1 (bd-9nw): Documentation** 
- Comprehensive sandbox troubleshooting section in TROUBLESHOOTING.md
  - Detailed symptoms, root causes, and escape hatches
  - Step-by-step troubleshooting workflow
  - Comparison table for --sandbox, --force, and --allow-stale flags
- Global flags section added to CLI_REFERENCE.md
  - Documents --sandbox, --allow-stale, and --force flags
  - Usage examples and when to use each flag
- GitHub issue #353 comment with immediate workarounds

**Phase 2 (bd-u3t): Sandbox Auto-Detection** 
- Automatic sandbox detection using syscall.Kill permission checks
  - cmd/bd/sandbox_unix.go: Unix/Linux/macOS implementation
  - cmd/bd/sandbox_windows.go: Windows stub (conservative approach)
  - cmd/bd/sandbox_test.go: Comprehensive test coverage
- Auto-enables sandbox mode when detected
  - Shows: "ℹ️  Sandbox detected, using direct mode"
  - Respects explicit --sandbox or --no-daemon flags
- Updated documentation to reflect auto-detection (v0.21.1+)

**Phase 3 (bd-e0o): Enhanced Daemon Robustness** 
- Permission-aware process checks in cmd/bd/daemon_unix.go
  - Correctly handles EPERM (operation not permitted) from syscall.Kill
  - Treats EPERM as "process exists but not signable" = running
  - Prevents false negatives in sandboxed environments
- Metadata health check in cmd/bd/daemon_event_loop.go
  - Periodic verification that metadata is accessible
  - Helps detect external import operations (bd import --force)
  - Non-fatal logging for diagnostics
- Comprehensive test suite in cmd/bd/daemon_unix_test.go
  - Self-check, init process, nonexistent process, parent process tests

**Impact:**
- Codex users: No manual intervention needed, auto-detected
- Stuck states: Three escape hatches (--sandbox, --force, --allow-stale)
- Daemon robustness: Handles permission-restricted environments gracefully
- All three follow-up issues (bd-9nw, bd-u3t, bd-e0o) closed

**Files changed:**
- cmd/bd/main.go: Auto-detection logic in PersistentPreRun
- cmd/bd/sandbox_unix.go: Unix sandbox detection (new)
- cmd/bd/sandbox_windows.go: Windows sandbox detection stub (new)
- cmd/bd/sandbox_test.go: Sandbox detection tests (new)
- cmd/bd/daemon_unix.go: Permission-aware isProcessRunning()
- cmd/bd/daemon_unix_test.go: Process check tests (new)
- cmd/bd/daemon_event_loop.go: Metadata health check
- docs/TROUBLESHOOTING.md: Comprehensive sandbox section
- docs/CLI_REFERENCE.md: Global flags documentation

Closes bd-9nw, bd-u3t, bd-e0o
Related: GH #353

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 19:32:45 -05:00

51 lines
1.4 KiB
Go

//go:build unix || linux || darwin
package main
import (
"os"
"os/exec"
"syscall"
)
var daemonSignals = []os.Signal{syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP}
// configureDaemonProcess sets up platform-specific process attributes for daemon
func configureDaemonProcess(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
}
func sendStopSignal(process *os.Process) error {
return process.Signal(syscall.SIGTERM)
}
func isReloadSignal(sig os.Signal) bool {
return sig == syscall.SIGHUP
}
// isProcessRunning checks if a process with the given PID is running.
// Permission-aware: handles EPERM (operation not permitted) correctly.
//
// In sandboxed environments, syscall.Kill may return EPERM even when the process
// exists. We treat EPERM as "process exists but we can't signal it", which means
// it's still running from our perspective.
//
// Implements bd-e0o: Phase 3 permission-aware process checks for GH #353
func isProcessRunning(pid int) bool {
err := syscall.Kill(pid, 0)
if err == nil {
// No error = process exists and we can signal it
return true
}
if err == syscall.EPERM {
// EPERM = operation not permitted
// Process exists but we don't have permission to signal it
// This happens in sandboxed environments (Codex, containers)
// Treat this as "process is running"
return true
}
// ESRCH = no such process
// Any other error = process not running
return false
}