ruvnet / ruvnet/ruflo

Bug: `claude-flow status` shows STOPPED even when daemon is running

Open
#984 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
72.7k
Forks
8.6k
Avg merge
2d 23h
Merged PRs (30d)
83

Description

# Bug: `claude-flow status` shows STOPPED even when daemon is running

## Summary

Running `claude-flow status` always shows `[STOPPED]` with all metrics at zero, even when:
- Daemon is confirmed running (`ps aux` shows the process)
- `claude-flow daemon start` reports success
- `claude-flow doctor` passes all checks

## Environment

- **Version**: @claude-flow/cli@3.0.0-alpha.158
- **OS**: macOS (Darwin 24.0.0)
- **Node**: v23.10.0

## Steps to Reproduce

```bash
# Start daemon
npx @claude-flow/cli@latest daemon start
# Output: [OK] Daemon started in background (PID: 18824)

# Verify daemon is running
ps aux | grep 18824
# Output: node ... daemon start --foreground --quiet

# Check status
npx @claude-flow/cli@latest status
# Output: Claude Flow V3 [STOPPED] ← BUG: should show [RUNNING]
```

## Root Cause

Two issues in the codebase:

### Issue 1: Missing `task_summary` MCP tool

**File**: `src/mcp-tools/task-tools.ts`

The `status.ts` command calls `task_summary` but this tool doesn't exist. Only these tools are defined:
- `task_create`, `task_status`, `task_list`, `task_complete`, `task_update`, `task_cancel`

### Issue 2: Monolithic error handling in `getSystemStatus()`

**File**: `src/commands/status.ts` (around line 69)

The entire function is wrapped in ONE try-catch. If ANY MCP tool fails, it returns "stopped":

```typescript
async function getSystemStatus() {
try {
const swarmStatus = await callMCPTool('swarm_status', {});
const memoryStatus = await callMCPTool('memory_stats', {});
const taskStatus = await callMCPTool('task_summary', {}); // ← FAILS: tool doesn't exist
// ... build status object
} catch (error) {
// ANY error → return stopped status
return { running: false, ... };
}
}
```

## Fix

### Fix 1: Add `task_summary` tool

In `src/mcp-tools/task-tools.ts`, add this tool definition to the `taskTools` array:

```typescript
{
name: 'task_summary',
description: 'Get task summary counts by status',
category: 'task',
inputSchema: {
type: 'object',
properties: {},
},
handler: async () => {
const store = loadTaskStore();
const tasks = Object.values(store.tasks);
return {
total: tasks.length,
pending: tasks.filter(t => t.status === 'pending').length,
running: tasks.filter(t => t.status === 'in_progress' || t.status === 'running').length,
completed: tasks.filter(t => t.status === 'completed').length,
failed: tasks.filter(t => t.status === 'failed').length,
cancelled: tasks.filter(t => t.status === 'cancelled').length,
};
},
},
```

### Fix 2: Granular error handling in status.ts

Replace the monolithic try-catch with individual try-catch per service. This way, if one service fails, others still report correctly:

```typescript
async function getSystemStatus() {
const status = {
initialized: true,
running: false,
swarm: { id: null, topology: 'none', agents: { total: 0, active: 0, idle: 0 }, health: 'stopped', uptime: 0 },
mcp: { running: false, port: null, transport: 'stdio' },
memory: { entries: 0, size: '0 B', backend: 'none', performance: { searchTime: 0, cacheHitRate: 0 } },
tasks: { total: 0, pending: 0, running: 0, completed: 0, failed: 0 },
performance: { cpuUsage: getProcessCpuUsage(), memoryUsage: getProcessMemoryUsage(), flashAttention: 'N/A', searchSpeed: 'N/A' }
};

let anyServiceRunning = false;

// Swarm (with fallback)
try {
const swarmStatus = await callMCPTool('swarm_status', { includeMetrics: true });
if (swarmStatus) {
anyServiceRunning = true;
status.swarm = {
id: swarmStatus.swarmId || swarmStatus.id || null,
topology: swarmStatus.topology || 'unknown',
agents: swarmStatus.agents || { total: swarmStatus.agentCount || 0, active: 0, idle: 0 },
health: swarmStatus.health || (swarmStatus.status === 'running' ? 'healthy' : 'stopped'),
uptime: swarmStatus.uptime || 0
};
}
} catch { /* swarm not running */ }

// MCP (with fallback)
try {
const mcp = await callMCPTool('mcp_status', {});
if (mcp) { anyServiceRunning = true; status.mcp = mcp; }
} catch { /* MCP not running */ }

// Memory (with fallback + field name handling)
try {
const memoryStatus = await callMCPTool('memory_stats', {});
if (memoryStatus) {
anyServiceRunning = true;
const entries = memoryStatus.entries ?? memoryStatus.totalEntries ?? 0;
const size = typeof memoryStatus.size === 'number' ? memoryStatus.size : 0;
status.memory = {
entries,
size: formatBytes(size),
backend: memoryStatus.backend || 'unknown',
performance: {
searchTime: memoryStatus.performance?.avgSearchTime ?? 0,
cacheHitRate: memoryStatus.performance?.cacheHitRate ?? 0
}
};
}
} catch { /* memory not available */ }

// Tasks (with fallback to task_list)
try {
const taskStatus = await callMCPTool('task_summary', {});
if (taskStatus) { anyServiceRunning = true; status.tasks = taskStatus; }
} catch {
try {
const taskList = await callMCPTool('task_list', {});
if (taskList?.tasks) {
anyServiceRunning = true;
const tasks = taskList.tasks;
status.tasks = {
total: tasks.length,
pending: tasks.filter(t => t.status === 'pending').length,
running: tasks.filter(t => t.status === 'in_progress' || t.status === 'running').length,
completed: tasks.filter(t => t.status === 'completed').length,
failed: tasks.filter(t => t.status === 'failed').length
};
}
} catch { /* tasks not available */ }
}

status.running = anyServiceRunning;
if (anyServiceRunning) {
status.performance.flashAttention = '2.8x speedup';
status.performance.searchSpeed = '150x faster';
}

return status;
}
```

## Tested Fix

I applied these fixes locally to the npx cache and confirmed:

```bash
$ npx @claude-flow/cli@latest status

Claude Flow V3 [RUNNING] ← Now works!

Swarm
+----------+---------+
| Property | Value |
+----------+---------+
| Topology | unknown |
| Health | healthy |
+----------+---------+

Tasks
+-----------+-------+
| Pending | 1 | ← Correctly counts tasks
| Total | 1 |
+-----------+-------+

Memory
+----------------+-------+
| Backend | file | ← Correctly detects backend
+----------------+-------+
```

## Bonus Bug Found: Memory Backend Mismatch

While investigating, I found a separate issue:

- `memory_stats` MCP tool reads from hardcoded path `.claude-flow/memory/store.json`
- CLI `memory store` command writes to agentdb (different backend)
- Config specifies `persistPath: .claude-flow/data`

This causes `memory_stats` to always show 0 entries even after storing data. Separate issue, lower priority.

---

**Files to modify:**
1. `v3/@claude-flow/cli/src/mcp-tools/task-tools.ts` - add `task_summary`
2. `v3/@claude-flow/cli/src/commands/status.ts` - granular error handling

Contributor guide

Open the contributing guide

Research direction

Start by reading src/commands/status.ts around getSystemStatus() and src/mcp-tools/task-tools.ts, then run the reported daemon start and status commands. Confirm that status remains RUNNING when an individual MCP lookup fails, that task counts are available, and that the relevant existing checks pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.