Description
The daemon worker system (worker-daemon.js) has resource check thresholds (maxCpuLoad: 2.0 and minFreeMemoryPercent: 20) in canRunWorker() that are incompatible with macOS, causing all workers to be permanently deferred and never execute.
Root Cause
1. maxCpuLoad: 2.0 is too low for multi-core systems
On macOS (and Linux), os.loadavg()[0] returns the 1-minute system load average across all cores. On an 8-core Mac under normal development workload, load averages of 8-16 are typical. The default threshold of 2.0 means the daemon defers all workers with "CPU load too high" on any modern multi-core machine that isn't essentially idle.
Suggested fix: Use os.cpus().length * 2 (e.g., 16 on 8-core) or a much higher default like 20-24.
2. minFreeMemoryPercent: 20 is incompatible with macOS memory management
On macOS, os.freemem() returns only truly unused RAM (wired + active + speculative excluded), not including the large amount of reclaimable cached/purgeable memory. A typical Mac reports only 1-5% "free" memory even when plenty is available via cache reclamation. The 20% threshold means workers are always deferred with "Memory too low: 1.0% free" on macOS.
Suggested fix: Either:
- Disable the memory check on macOS (
process.platform === 'darwin')
- Lower the threshold significantly (e.g., 1%)
- Use
vm_stat or a platform-aware memory check on macOS
3. Deadlock amplification
When all workers are deferred, they go into pendingWorkers queue. But processPendingWorkers() is only called in executeWorker()'s finally block. Since no worker ever executes, deferred workers are stuck forever. (Related to #1052 which fixes the busy-wait aspect, but not the root cause threshold issue.)
4. --quiet flag hardcoded in background spawn
In daemon.js line 186, the background daemon spawn hardcodes --quiet, suppressing all worker event output to the log file. This makes it impossible to diagnose why workers aren't running.
// daemon.js line 184-186
const child = spawn(process.execPath, [
cliPath,
'daemon', 'start', '--foreground', '--quiet' // <-- --quiet hardcoded
], { ... });
Steps to Reproduce
- Run
claude-flow daemon start on macOS
- Check
daemon-state.json - all workers show runCount: 0
- Wait indefinitely - workers never execute
Expected Behavior
Workers should execute on their scheduled intervals on a typical macOS development machine.
Environment
- macOS (tested on macOS 15.3, 8-core Apple Silicon)
- Node.js v22.15.0
@claude-flow/cli v3.0.0 (alpha)
Proposed Fix
See PR (linked below) with:
maxCpuLoad changed from 2.0 to os.cpus().length * 3 (dynamic based on core count)
minFreeMemoryPercent changed from 20 to 1 (accommodates macOS memory reporting)
- Removed hardcoded
--quiet flag from background daemon spawn
Description
The daemon worker system (
worker-daemon.js) has resource check thresholds (maxCpuLoad: 2.0andminFreeMemoryPercent: 20) incanRunWorker()that are incompatible with macOS, causing all workers to be permanently deferred and never execute.Root Cause
1.
maxCpuLoad: 2.0is too low for multi-core systemsOn macOS (and Linux),
os.loadavg()[0]returns the 1-minute system load average across all cores. On an 8-core Mac under normal development workload, load averages of 8-16 are typical. The default threshold of2.0means the daemon defers all workers with "CPU load too high" on any modern multi-core machine that isn't essentially idle.Suggested fix: Use
os.cpus().length * 2(e.g., 16 on 8-core) or a much higher default like 20-24.2.
minFreeMemoryPercent: 20is incompatible with macOS memory managementOn macOS,
os.freemem()returns only truly unused RAM (wired + active + speculative excluded), not including the large amount of reclaimable cached/purgeable memory. A typical Mac reports only 1-5% "free" memory even when plenty is available via cache reclamation. The 20% threshold means workers are always deferred with "Memory too low: 1.0% free" on macOS.Suggested fix: Either:
process.platform === 'darwin')vm_stator a platform-aware memory check on macOS3. Deadlock amplification
When all workers are deferred, they go into
pendingWorkersqueue. ButprocessPendingWorkers()is only called inexecuteWorker()'sfinallyblock. Since no worker ever executes, deferred workers are stuck forever. (Related to #1052 which fixes the busy-wait aspect, but not the root cause threshold issue.)4.
--quietflag hardcoded in background spawnIn
daemon.jsline 186, the background daemon spawn hardcodes--quiet, suppressing all worker event output to the log file. This makes it impossible to diagnose why workers aren't running.Steps to Reproduce
claude-flow daemon starton macOSdaemon-state.json- all workers showrunCount: 0Expected Behavior
Workers should execute on their scheduled intervals on a typical macOS development machine.
Environment
@claude-flow/cliv3.0.0 (alpha)Proposed Fix
See PR (linked below) with:
maxCpuLoadchanged from2.0toos.cpus().length * 3(dynamic based on core count)minFreeMemoryPercentchanged from20to1(accommodates macOS memory reporting)--quietflag from background daemon spawn