Files
beads/docs/DAEMON.md
Charles P. Cross a69e94a958 Auto-disable daemon in git worktrees for safety (#567)
* feat: auto-disable daemon in git worktrees for safety

Implement worktree daemon compatibility as proposed in the analysis.
The daemon is now automatically disabled when running in a git worktree
unless sync-branch is configured.

Git worktrees share the same .beads directory, and the daemon commits
to whatever branch its working directory has checked out. This causes
commits to go to the wrong branch when using daemon in worktrees.

- Add shouldDisableDaemonForWorktree() helper that checks:
  1. If current directory is a git worktree (via git rev-parse)
  2. If sync-branch is configured (env var or config.yaml)
- Modify shouldAutoStartDaemon() to call the helper
- Modify daemon connection logic in main.go to skip connection
- Add FallbackWorktreeSafety constant for daemon status reporting
- Update warnWorktreeDaemon() to skip warning when sync-branch configured

- In worktree WITHOUT sync-branch: daemon auto-disabled, direct mode used
- In worktree WITH sync-branch: daemon enabled (commits go to dedicated branch)
- In regular repo: no change (daemon works as before)

- Added comprehensive unit tests for shouldDisableDaemonForWorktree()
- Added integration tests for shouldAutoStartDaemon() in worktree contexts
- Manual E2E testing verified correct behavior

- Updated WORKTREES.md with new automatic safety behavior
- Updated DAEMON.md with Git Worktrees section

* feat: check database config for sync-branch in worktree safety logic

Previously, the worktree daemon safety check only looked at:
- BEADS_SYNC_BRANCH environment variable
- sync-branch in config.yaml

This meant users who configured sync-branch via `bd config set sync-branch`
(which stores in the database) would still have daemon disabled in worktrees.

Now the check also reads sync.branch from the database config table,
making daemon work in worktrees when sync-branch is configured via any method.

Changes:
- Add IsConfiguredWithDB() function that checks env, config.yaml, AND database
- Add findBeadsDB() to locate database (worktree-aware via git-common-dir)
- Add getMainRepoRoot() helper using git rev-parse
- Add getConfigFromDB() for lightweight database reads
- Update shouldDisableDaemonForWorktree() to use IsConfiguredWithDB()
- Update warnWorktreeDaemon() to use IsConfiguredWithDB()
- Add test case for database config path

* refactor: use existing beads.FindDatabasePath() instead of duplicating code

Remove duplicate getMainRepoRoot() and findBeadsDB() functions from
syncbranch.go and use the existing beads.FindDatabasePath() which is
already worktree-aware.

Changes:
- Replace custom findBeadsDB() with beads.FindDatabasePath()
- Remove duplicate getMainRepoRoot() (git.GetMainRepoRoot() exists)
- Remove unused imports (exec, strings, filepath)
- Clean up debug logging in tests

---------

Co-authored-by: Charles P. Cross <cpdata@users.noreply.github.com>
2025-12-16 00:06:19 -08:00

548 lines
14 KiB
Markdown

# Daemon Management Guide
**For:** AI agents and developers managing bd background processes
**Version:** 0.21.0+
## Overview
bd runs a background daemon per workspace for auto-sync, RPC operations, and real-time monitoring. This guide covers daemon management, event-driven mode, and troubleshooting.
## Do I Need the Daemon?
**TL;DR:** For most users, the daemon runs automatically and you don't need to think about it.
### When Daemon Helps (default: enabled)
| Scenario | Benefit |
|----------|---------|
| **Multi-agent workflows** | Prevents database locking conflicts |
| **Team collaboration** | Auto-syncs JSONL to git in background |
| **Long coding sessions** | Changes saved even if you forget `bd sync` |
| **Real-time monitoring** | Enables `bd watch` and status updates |
### When to Disable Daemon
| Scenario | How to Disable |
|----------|----------------|
| **Git worktrees (no sync-branch)** | Auto-disabled for safety |
| **CI/CD pipelines** | `BEADS_NO_DAEMON=true` |
| **Offline work** | `--no-daemon` (no git push available) |
| **Resource-constrained** | `BEADS_NO_DAEMON=true` |
| **Deterministic testing** | Use exclusive lock (see below) |
### Git Worktrees and Daemon
**Automatic safety:** Daemon is automatically disabled in git worktrees unless sync-branch is configured. This prevents commits going to the wrong branch.
**Enable daemon in worktrees:** Configure sync-branch to safely use daemon across all worktrees:
```bash
bd config set sync-branch beads-metadata
```
With sync-branch configured, daemon commits to a dedicated branch using an internal worktree, so your current branch is never affected. See [WORKTREES.md](WORKTREES.md) for details.
### Local-Only Users
If you're working alone on a local project with no git remote:
- **Daemon still helps**: Batches writes, handles auto-export to JSONL
- **But optional**: Use `--no-daemon` if you prefer direct database access
- **No network calls**: Daemon doesn't phone home or require internet
```bash
# Check if daemon is running
bd info | grep daemon
# Force direct mode for one command
bd --no-daemon list
# Disable for entire session
export BEADS_NO_DAEMON=true
```
## Architecture
**Per-Workspace Model (LSP-style):**
```
MCP Server (one instance)
Per-Project Daemons (one per workspace)
SQLite Databases (complete isolation)
```
Each workspace gets its own daemon:
- Socket at `.beads/bd.sock` (`.beads/bd.pipe` on Windows)
- Auto-starts on first command (unless disabled)
- Handles auto-sync, batching, background operations
- Complete database isolation (no cross-project pollution)
## Managing Daemons
### List All Running Daemons
```bash
# See all daemons across workspaces
bd daemons list --json
# Example output:
# [
# {
# "workspace": "/Users/alice/projects/webapp",
# "pid": 12345,
# "socket": "/Users/alice/projects/webapp/.beads/bd.sock",
# "version": "0.21.0",
# "uptime_seconds": 3600
# }
# ]
```
### Check Daemon Health
```bash
# Check for version mismatches, stale sockets
bd daemons health --json
# Example output:
# {
# "healthy": false,
# "issues": [
# {
# "workspace": "/Users/alice/old-project",
# "issue": "version_mismatch",
# "daemon_version": "0.20.0",
# "cli_version": "0.21.0"
# }
# ]
# }
```
**When to use:**
- After upgrading bd (check for version mismatches)
- Debugging sync issues
- Periodic health monitoring
### Stop/Restart Daemons
```bash
# Stop specific daemon by workspace path
bd daemons stop /path/to/workspace --json
# Stop by PID
bd daemons stop 12345 --json
# Restart (stop + auto-start on next command)
bd daemons restart /path/to/workspace --json
bd daemons restart 12345 --json
# Stop ALL daemons
bd daemons killall --json
bd daemons killall --force --json # Force kill if graceful fails
```
### View Daemon Logs
```bash
# View last 100 lines
bd daemons logs /path/to/workspace -n 100
# Follow mode (tail -f style)
bd daemons logs 12345 -f
# Debug sync issues
bd daemons logs . -n 500 | grep -i "export\|import\|sync"
```
**Common log patterns:**
- `[INFO] Auto-sync: export complete` - Successful JSONL export
- `[WARN] Git push failed: ...` - Push error (auto-retry)
- `[ERROR] Version mismatch` - Daemon/CLI version out of sync
## Version Management
**Automatic Version Checking (v0.16.0+):**
bd automatically handles daemon version mismatches:
- Version compatibility checked on every connection
- Old daemons automatically detected and restarted
- No manual intervention needed after upgrades
- Works with MCP server and CLI
**After upgrading bd:**
```bash
# 1. Check for mismatches
bd daemons health --json
# 2. Restart all daemons with new version
bd daemons killall
# 3. Next bd command auto-starts daemon with new version
bd ready
```
**Troubleshooting version mismatches:**
- Daemon won't stop: `bd daemons killall --force`
- Socket file stale: `rm .beads/bd.sock` (auto-cleans on next start)
- Multiple bd versions installed: `which bd` and `bd version`
## Event-Driven Daemon Mode (Experimental)
**NEW in v0.16+**: Event-driven mode replaces 5-second polling with instant reactivity.
### Benefits
-**<500ms latency** (vs ~5000ms with polling)
- 🔋 **~60% less CPU usage** (no continuous polling)
- 🎯 **Instant sync** on mutations and file changes
- 🛡️ **Dropped events safety net** prevents data loss
### How It Works
**Architecture:**
```
FileWatcher (platform-native)
├─ .beads/issues.jsonl (file changes)
├─ .git/refs/heads (git updates)
└─ RPC mutations (create, update, close)
Debouncer (500ms batch window)
Export → Git Commit/Push
```
**Platform-native APIs:**
- Linux: `inotify`
- macOS: `FSEvents` (via kqueue)
- Windows: `ReadDirectoryChangesW`
**Mutation events** from RPC trigger immediate export
**Debouncer** batches rapid changes (500ms window) to avoid export storms
**Polling fallback** if fsnotify unavailable (network filesystems)
### Enabling Event-Driven Mode
**Opt-In (Phase 1):**
```bash
# Enable for single daemon
BEADS_DAEMON_MODE=events bd daemon --start
# Set globally in shell profile
export BEADS_DAEMON_MODE=events
# Restart all daemons to apply
bd daemons killall
# Next bd command auto-starts with new mode
```
**Available modes:**
- `poll` (default) - Traditional 5-second polling, stable and battle-tested
- `events` - Event-driven mode, experimental but thoroughly tested
### Configuration
**Environment Variables:**
| Variable | Values | Default | Description |
|----------|--------|---------|-------------|
| `BEADS_DAEMON_MODE` | `poll`, `events` | `poll` | Daemon operation mode |
| `BEADS_WATCHER_FALLBACK` | `true`, `false` | `true` | Fall back to polling if fsnotify fails |
**Disable polling fallback (require fsnotify):**
```bash
# Fail if watcher unavailable (e.g., testing)
BEADS_WATCHER_FALLBACK=false BEADS_DAEMON_MODE=events bd daemon --start
```
**Switch back to polling:**
```bash
# Explicitly use polling mode
BEADS_DAEMON_MODE=poll bd daemon --start
# Or unset to use default
unset BEADS_DAEMON_MODE
bd daemons killall # Restart with default (poll) mode
```
### Troubleshooting Event-Driven Mode
**If watcher fails to start:**
```bash
# Check daemon logs for errors
bd daemons logs /path/to/workspace -n 100
# Common error patterns:
# - "File watcher unavailable: ..." - fsnotify init failed
# - "Falling back to polling" - watcher disabled, using polls
# - "Resource limit exceeded" - too many open files
```
**Common causes:**
1. **Network filesystem** (NFS, SMB) - fsnotify may not work
- Solution: Use polling mode or local filesystem
2. **Container environment** - may need privileged mode
- Solution: Add `--privileged` or specific capabilities
3. **Resource limits** - check `ulimit -n` (open file descriptors)
- Solution: Increase limit: `ulimit -n 4096`
4. **WSL/virtualization** - reduced fsnotify reliability
- Solution: Test in native environment or use polling
**Fallback behavior:**
If `BEADS_DAEMON_MODE=events` but watcher fails:
- Daemon automatically falls back to polling (if `BEADS_WATCHER_FALLBACK=true`)
- Warning logged: `File watcher unavailable, falling back to polling`
- All functionality works normally (just higher latency)
### Performance Comparison
| Metric | Polling Mode | Event-Driven Mode |
|--------|--------------|-------------------|
| Sync Latency | ~5000ms | <500ms |
| CPU Usage | ~2-3% (continuous) | ~0.5% (idle) |
| Memory | 30MB | 35MB (+5MB for watcher) |
| File Events | Polled every 5s | Instant detection |
| Git Updates | Polled every 5s | Instant detection |
**Future (Phase 2):** Event-driven mode will become default once proven stable in production.
## Auto-Start Behavior
**Default (v0.9.11+):** Daemon auto-starts on first bd command
```bash
# No manual start needed
bd ready # Daemon starts automatically if not running
# Check status
bd info --json | grep daemon_running
```
**Disable auto-start:**
```bash
# Require manual daemon start
export BEADS_AUTO_START_DAEMON=false
# Start manually
bd daemon --start
```
**Auto-start with exponential backoff:**
- 1st attempt: immediate
- 2nd attempt: 100ms delay
- 3rd attempt: 200ms delay
- Max retries: 5
- Logs available: `bd daemons logs . -n 50`
## Daemon Configuration
**Environment Variables:**
| Variable | Values | Default | Description |
|----------|--------|---------|-------------|
| `BEADS_AUTO_START_DAEMON` | `true`, `false` | `true` | Auto-start daemon on commands |
| `BEADS_DAEMON_MODE` | `poll`, `events` | `poll` | Sync mode (polling vs events) |
| `BEADS_WATCHER_FALLBACK` | `true`, `false` | `true` | Fall back to poll if events fail |
| `BEADS_NO_DAEMON` | `true`, `false` | `false` | Disable daemon entirely (direct DB) |
**Example configurations:**
```bash
# Force direct mode (no daemon)
export BEADS_NO_DAEMON=true
# Event-driven with strict requirements
export BEADS_DAEMON_MODE=events
export BEADS_WATCHER_FALLBACK=false
# Disable auto-start (manual control)
export BEADS_AUTO_START_DAEMON=false
```
## Git Worktrees Warning
**⚠️ Important Limitation:** Daemon mode does NOT work correctly with `git worktree`.
**The Problem:**
- Git worktrees share the same `.git` directory and `.beads` database
- Daemon doesn't know which branch each worktree has checked out
- Can commit/push to wrong branch
**Solutions:**
1. **Use `--no-daemon` flag** (recommended):
```bash
bd --no-daemon ready
bd --no-daemon create "Fix bug" -p 1
```
2. **Disable via environment** (entire session):
```bash
export BEADS_NO_DAEMON=1
bd ready # All commands use direct mode
```
3. **Disable auto-start** (less safe):
```bash
export BEADS_AUTO_START_DAEMON=false
```
**Automatic detection:** bd detects worktrees and warns if daemon is active.
See [GIT_INTEGRATION.md](GIT_INTEGRATION.md) for more details.
## Exclusive Lock Protocol (Advanced)
**For external tools that need full database control** (e.g., CI/CD, deterministic execution).
When `.beads/.exclusive-lock` file exists:
- Daemon skips all operations for the locked database
- External tool has complete control over git sync and database
- Stale locks (dead process) auto-cleaned
**Lock file format (JSON):**
```json
{
"holder": "my-tool",
"pid": 12345,
"hostname": "build-server",
"started_at": "2025-11-08T08:00:00Z",
"version": "1.0.0"
}
```
**Quick example:**
```bash
# Create lock
echo '{"holder":"my-tool","pid":'$$',"hostname":"'$(hostname)'","started_at":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","version":"1.0.0"}' > .beads/.exclusive-lock
# Do work (daemon won't interfere)
bd create "My issue" -p 1
# Release lock
rm .beads/.exclusive-lock
```
**Use cases:**
- VibeCoder (deterministic execution)
- CI/CD pipelines (controlled sync timing)
- Testing frameworks (isolated test runs)
See [EXCLUSIVE_LOCK.md](../EXCLUSIVE_LOCK.md) for complete documentation.
## Common Daemon Issues
### Stale Sockets
**Symptoms:** `bd ready` shows "daemon not responding"
**Solutions:**
```bash
# Auto-cleanup on next command
bd daemons list # Removes stale sockets
# Manual cleanup
rm .beads/bd.sock
bd ready # Auto-starts fresh daemon
```
### Version Mismatch
**Symptoms:** `bd ready` shows "version mismatch" error
**Solutions:**
```bash
# Check versions
bd version
bd daemons health --json
# Restart all daemons
bd daemons killall
bd ready # Auto-starts with CLI version
```
### Daemon Won't Stop
**Symptoms:** `bd daemons stop` hangs or times out
**Solutions:**
```bash
# Force kill
bd daemons killall --force
# Nuclear option (all bd processes)
pkill -9 bd
# Clean up socket
rm .beads/bd.sock
```
### Memory Leaks
**Symptoms:** Daemon process grows to 100+ MB
**Solutions:**
```bash
# Check current memory usage
ps aux | grep "bd daemon"
# Restart daemon
bd daemons restart .
# Check logs for issues
bd daemons logs . -n 200 | grep -i "memory\|leak\|oom"
```
**Expected memory usage:**
- Baseline: ~30MB
- With watcher: ~35MB
- Per issue: ~500 bytes (10K issues = ~5MB)
## Multi-Workspace Best Practices
### When managing multiple projects:
```bash
# Check all daemons
bd daemons list --json
# Stop unused workspaces to free resources
bd daemons stop /path/to/old-project
# Health check before critical work
bd daemons health --json
# Clean restart after major upgrades
bd daemons killall
# Daemons restart on next command per workspace
```
### Resource limits:
- Each daemon: ~30-35MB memory
- 10 workspaces: ~300-350MB total
- CPU: <1% per daemon (idle), 2-3% (active sync)
- File descriptors: ~10 per daemon
### When to disable daemons:
- ✅ Git worktrees (use `--no-daemon`)
- ✅ Embedded/resource-constrained environments
- ✅ Testing/CI (deterministic execution)
- ✅ Offline work (no git push available)
## See Also
- [AGENTS.md](../AGENTS.md) - Main agent workflow guide
- [EXCLUSIVE_LOCK.md](../EXCLUSIVE_LOCK.md) - External tool integration
- [GIT_INTEGRATION.md](GIT_INTEGRATION.md) - Git workflow and merge strategies
- [commands/daemons.md](../commands/daemons.md) - Daemon command reference