@sparkleideas/claude-flow-patch 3.1.0-alpha.44.patch.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/AGENTS.md +162 -0
  2. package/CLAUDE.md +506 -0
  3. package/README.md +351 -0
  4. package/bin/claude-flow-patch.mjs +148 -0
  5. package/check-patches.sh +195 -0
  6. package/lib/categories.json +15 -0
  7. package/lib/common.py +97 -0
  8. package/lib/discover.mjs +181 -0
  9. package/lib/discover.sh +160 -0
  10. package/package.json +86 -0
  11. package/patch/010-CF-001-doctor-yaml/README.md +11 -0
  12. package/patch/010-CF-001-doctor-yaml/fix.py +20 -0
  13. package/patch/010-CF-001-doctor-yaml/sentinel +1 -0
  14. package/patch/020-CF-002-config-export-yaml/README.md +11 -0
  15. package/patch/020-CF-002-config-export-yaml/fix.py +130 -0
  16. package/patch/020-CF-002-config-export-yaml/sentinel +1 -0
  17. package/patch/030-DM-001-daemon-log-zero/README.md +12 -0
  18. package/patch/030-DM-001-daemon-log-zero/fix.py +37 -0
  19. package/patch/030-DM-001-daemon-log-zero/sentinel +1 -0
  20. package/patch/040-DM-002-cpu-load-threshold/README.md +11 -0
  21. package/patch/040-DM-002-cpu-load-threshold/fix.py +6 -0
  22. package/patch/040-DM-002-cpu-load-threshold/sentinel +1 -0
  23. package/patch/050-DM-003-macos-freemem/README.md +11 -0
  24. package/patch/050-DM-003-macos-freemem/fix.py +7 -0
  25. package/patch/050-DM-003-macos-freemem/sentinel +1 -0
  26. package/patch/060-DM-004-preload-worker-stub/README.md +11 -0
  27. package/patch/060-DM-004-preload-worker-stub/fix.py +34 -0
  28. package/patch/060-DM-004-preload-worker-stub/sentinel +1 -0
  29. package/patch/070-DM-005-consolidation-worker-stub/README.md +11 -0
  30. package/patch/070-DM-005-consolidation-worker-stub/fix.py +46 -0
  31. package/patch/070-DM-005-consolidation-worker-stub/sentinel +1 -0
  32. package/patch/080-EM-001-embedding-ignores-config/README.md +11 -0
  33. package/patch/080-EM-001-embedding-ignores-config/fix.py +111 -0
  34. package/patch/080-EM-001-embedding-ignores-config/sentinel +1 -0
  35. package/patch/090-EM-002-transformers-cache-eacces/README.md +11 -0
  36. package/patch/090-EM-002-transformers-cache-eacces/fix.sh +12 -0
  37. package/patch/090-EM-002-transformers-cache-eacces/sentinel +1 -0
  38. package/patch/100-GV-001-hnsw-ghost-vectors/README.md +11 -0
  39. package/patch/100-GV-001-hnsw-ghost-vectors/fix.py +34 -0
  40. package/patch/100-GV-001-hnsw-ghost-vectors/sentinel +1 -0
  41. package/patch/110-HK-001-post-edit-file-path/README.md +44 -0
  42. package/patch/110-HK-001-post-edit-file-path/fix.py +23 -0
  43. package/patch/110-HK-001-post-edit-file-path/sentinel +1 -0
  44. package/patch/120-HK-002-hooks-tools-stub/README.md +36 -0
  45. package/patch/120-HK-002-hooks-tools-stub/fix.py +155 -0
  46. package/patch/120-HK-002-hooks-tools-stub/sentinel +1 -0
  47. package/patch/130-HK-003-metrics-hardcoded/README.md +30 -0
  48. package/patch/130-HK-003-metrics-hardcoded/fix.py +82 -0
  49. package/patch/130-HK-003-metrics-hardcoded/sentinel +1 -0
  50. package/patch/135-HK-004-respect-daemon-autostart/README.md +11 -0
  51. package/patch/135-HK-004-respect-daemon-autostart/fix.py +14 -0
  52. package/patch/135-HK-004-respect-daemon-autostart/sentinel +1 -0
  53. package/patch/137-HK-005-daemon-pid-guard/README.md +11 -0
  54. package/patch/137-HK-005-daemon-pid-guard/fix.py +53 -0
  55. package/patch/137-HK-005-daemon-pid-guard/sentinel +2 -0
  56. package/patch/140-HW-001-stdin-hang/README.md +11 -0
  57. package/patch/140-HW-001-stdin-hang/fix.py +6 -0
  58. package/patch/140-HW-001-stdin-hang/sentinel +1 -0
  59. package/patch/150-HW-002-failures-swallowed/README.md +11 -0
  60. package/patch/150-HW-002-failures-swallowed/fix.py +42 -0
  61. package/patch/150-HW-002-failures-swallowed/sentinel +1 -0
  62. package/patch/160-HW-003-aggressive-intervals/README.md +11 -0
  63. package/patch/160-HW-003-aggressive-intervals/fix.py +52 -0
  64. package/patch/160-HW-003-aggressive-intervals/sentinel +3 -0
  65. package/patch/170-IN-001-intelligence-stub/README.md +64 -0
  66. package/patch/170-IN-001-intelligence-stub/fix.py +63 -0
  67. package/patch/170-IN-001-intelligence-stub/sentinel +1 -0
  68. package/patch/180-MM-001-memory-persist-path/README.md +27 -0
  69. package/patch/180-MM-001-memory-persist-path/fix.py +54 -0
  70. package/patch/180-MM-001-memory-persist-path/sentinel +1 -0
  71. package/patch/190-NS-001-discovery-default-namespace/README.md +16 -0
  72. package/patch/190-NS-001-discovery-default-namespace/fix.py +68 -0
  73. package/patch/190-NS-001-discovery-default-namespace/sentinel +2 -0
  74. package/patch/200-NS-002-targeted-require-namespace/README.md +19 -0
  75. package/patch/200-NS-002-targeted-require-namespace/fix.py +158 -0
  76. package/patch/200-NS-002-targeted-require-namespace/sentinel +2 -0
  77. package/patch/210-NS-003-namespace-typo-pattern/README.md +15 -0
  78. package/patch/210-NS-003-namespace-typo-pattern/fix.py +23 -0
  79. package/patch/210-NS-003-namespace-typo-pattern/sentinel +1 -0
  80. package/patch/220-RS-001-better-sqlite3-node24/README.md +54 -0
  81. package/patch/220-RS-001-better-sqlite3-node24/fix.py +27 -0
  82. package/patch/220-RS-001-better-sqlite3-node24/rebuild.sh +31 -0
  83. package/patch/220-RS-001-better-sqlite3-node24/sentinel +2 -0
  84. package/patch/230-RV-001-force-learn-tick/README.md +31 -0
  85. package/patch/230-RV-001-force-learn-tick/fix.py +14 -0
  86. package/patch/230-RV-001-force-learn-tick/sentinel +2 -0
  87. package/patch/240-RV-002-trajectory-load/README.md +28 -0
  88. package/patch/240-RV-002-trajectory-load/fix.py +14 -0
  89. package/patch/240-RV-002-trajectory-load/sentinel +2 -0
  90. package/patch/250-RV-003-trajectory-stats-sync/README.md +31 -0
  91. package/patch/250-RV-003-trajectory-stats-sync/fix.py +18 -0
  92. package/patch/250-RV-003-trajectory-stats-sync/sentinel +2 -0
  93. package/patch/260-SG-001-init-settings/README.md +29 -0
  94. package/patch/260-SG-001-init-settings/fix.py +143 -0
  95. package/patch/260-SG-001-init-settings/sentinel +4 -0
  96. package/patch/270-SG-003-init-helpers-all-paths/README.md +60 -0
  97. package/patch/270-SG-003-init-helpers-all-paths/fix.py +165 -0
  98. package/patch/270-SG-003-init-helpers-all-paths/sentinel +3 -0
  99. package/patch/280-UI-001-intelligence-stats-crash/README.md +11 -0
  100. package/patch/280-UI-001-intelligence-stats-crash/fix.py +57 -0
  101. package/patch/280-UI-001-intelligence-stats-crash/sentinel +1 -0
  102. package/patch/290-UI-002-neural-status-not-loaded/README.md +11 -0
  103. package/patch/290-UI-002-neural-status-not-loaded/fix.py +19 -0
  104. package/patch/290-UI-002-neural-status-not-loaded/sentinel +1 -0
  105. package/patch/300-DM-006-log-rotation/README.md +12 -0
  106. package/patch/300-DM-006-log-rotation/fix.py +72 -0
  107. package/patch/300-DM-006-log-rotation/sentinel +2 -0
  108. package/patch/310-HW-004-runwithtimeout-orphan/README.md +11 -0
  109. package/patch/310-HW-004-runwithtimeout-orphan/fix.py +10 -0
  110. package/patch/310-HW-004-runwithtimeout-orphan/sentinel +1 -0
  111. package/patch/320-SG-004-wizard-parity/README.md +40 -0
  112. package/patch/320-SG-004-wizard-parity/fix.py +208 -0
  113. package/patch/320-SG-004-wizard-parity/sentinel +3 -0
  114. package/patch/330-SG-005-start-all-subcommand/README.md +32 -0
  115. package/patch/330-SG-005-start-all-subcommand/fix.py +58 -0
  116. package/patch/330-SG-005-start-all-subcommand/sentinel +1 -0
  117. package/patch-all.sh +199 -0
  118. package/repair-post-init.sh +263 -0
  119. package/scripts/preflight.mjs +249 -0
  120. package/scripts/upstream-log.mjs +257 -0
@@ -0,0 +1,11 @@
1
+ # GV-001: HNSW ghost vectors persist after memory delete
2
+ **Severity**: Medium
3
+ **GitHub**: [#1122](https://github.com/ruvnet/claude-flow/issues/1122)
4
+ ## Root Cause
5
+ `deleteEntry()` soft-deletes the SQLite row but never removes the vector from the in-memory HNSW index or its persisted metadata. The search code (`searchHNSWIndex`) iterates HNSW results and looks up each ID in `hnswIndex.entries` Map — ghost vectors match but return stale metadata (key, namespace, content) because the Map entry was never removed.
6
+ ## Fix
7
+ After the SQLite soft-delete, remove the entry from `hnswIndex.entries` Map and save updated metadata. The HNSW vector DB (`@ruvector/core`) doesn't support point removal, but the search code already skips entries missing from the Map (`if (!entry) continue`), so removing from the Map is sufficient to suppress ghost results.
8
+ ## Files Patched
9
+ - memory/memory-initializer.js
10
+ ## Ops
11
+ 1 op in fix.py
@@ -0,0 +1,34 @@
1
+ # GV-001: Remove HNSW ghost vectors on memory delete
2
+ # GitHub: #1122
3
+ # After SQLite soft-delete, remove entry from persisted HNSW metadata file
4
+ # and in-memory map. Each CLI invocation is a fresh process so hnswIndex is
5
+ # usually null — the file-based cleanup is the primary path.
6
+ # 1 op
7
+
8
+ patch("GV-001: remove HNSW entry on delete",
9
+ MI,
10
+ """ // Get remaining count
11
+ const countResult = db.exec(`SELECT COUNT(*) FROM memory_entries WHERE status = 'active'`);
12
+ const remainingEntries = countResult[0]?.values?.[0]?.[0] || 0;
13
+ // Save updated database""",
14
+ """ // Remove ghost vector from HNSW metadata file
15
+ const entryId = String(checkResult[0].values[0][0]);
16
+ try {
17
+ const swarmDir = path.join(process.cwd(), '.swarm');
18
+ const metadataPath = path.join(swarmDir, 'hnsw.metadata.json');
19
+ if (fs.existsSync(metadataPath)) {
20
+ const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
21
+ const filtered = metadata.filter(([id]) => id !== entryId);
22
+ if (filtered.length < metadata.length) {
23
+ fs.writeFileSync(metadataPath, JSON.stringify(filtered));
24
+ }
25
+ }
26
+ } catch { /* best-effort */ }
27
+ // Also clear in-memory index if loaded
28
+ if (hnswIndex?.entries?.has(entryId)) {
29
+ hnswIndex.entries.delete(entryId);
30
+ }
31
+ // Get remaining count
32
+ const countResult = db.exec(`SELECT COUNT(*) FROM memory_entries WHERE status = 'active'`);
33
+ const remainingEntries = countResult[0]?.values?.[0]?.[0] || 0;
34
+ // Save updated database""")
@@ -0,0 +1 @@
1
+ grep "hnswIndex.entries.delete" memory/memory-initializer.js
@@ -0,0 +1,44 @@
1
+ # HK-001: post-edit hook records file_path as "unknown"
2
+
3
+ **Severity**: Medium
4
+ **GitHub**: [#1155](https://github.com/ruvnet/claude-flow/issues/1155)
5
+
6
+ ## Root Cause
7
+
8
+ The `hook-handler.cjs` template (in `helpers-generator.js`) reads the edited file
9
+ path from `process.env.TOOL_INPUT_file_path`. However, Claude Code's PostToolUse
10
+ hooks do **not** set individual `TOOL_INPUT_*` environment variables. Instead, tool
11
+ input is delivered via **stdin** as a JSON object:
12
+
13
+ ```json
14
+ {
15
+ "tool_name": "Edit",
16
+ "tool_input": { "file_path": "/path/to/file", ... },
17
+ "tool_response": { ... },
18
+ ...
19
+ }
20
+ ```
21
+
22
+ Because the env var is always empty, `recordEdit()` in `intelligence.cjs` logs
23
+ every edit as `file: "unknown"`. The data is consumed by `consolidate()` at session
24
+ end for edit-count analytics — cosmetic but wrong.
25
+
26
+ ## Fix
27
+
28
+ Two changes in `init/helpers-generator.js`:
29
+
30
+ 1. **Add stdin parsing** after the `argv` line — read and parse the JSON that
31
+ Claude Code pipes to PostToolUse hook commands.
32
+ 2. **Update post-edit handler** — read `stdinData.tool_input.file_path` instead
33
+ of `process.env.TOOL_INPUT_file_path`.
34
+
35
+ The `prompt` fallback for the `route` handler is also updated to prefer
36
+ `stdinData.tool_input.command` over the env var.
37
+
38
+ ## Files Patched
39
+
40
+ - init/helpers-generator.js
41
+
42
+ ## Ops
43
+
44
+ 2 ops in fix.py
@@ -0,0 +1,23 @@
1
+ # HK-001: post-edit hook records file_path as "unknown"
2
+ # Claude Code passes tool input via stdin JSON, not TOOL_INPUT_* env vars.
3
+ # 3 ops: add stdin parsing, fix prompt fallback, fix post-edit file extraction.
4
+
5
+ patch("HK-001a: add stdin parsing",
6
+ HELPERS_GEN,
7
+ """'const [,, command, ...args] = process.argv;',
8
+ "const prompt = process.env.PROMPT || process.env.TOOL_INPUT_command || args.join(' ') || '';",""",
9
+ """'const [,, command, ...args] = process.argv;',
10
+ '',
11
+ '// Read stdin JSON from Claude Code hooks (provides tool_input, tool_name, etc.)',
12
+ 'let stdinData = {};',
13
+ 'try {',
14
+ " const raw = require(\\'fs\\').readFileSync(0, \\'utf-8\\').trim();",
15
+ " if (raw) stdinData = JSON.parse(raw);",
16
+ '} catch (e) { /* stdin may be empty or non-JSON */ }',
17
+ '',
18
+ "const prompt = process.env.PROMPT || (stdinData.tool_input && stdinData.tool_input.command) || args.join(' ') || '';",""")
19
+
20
+ patch("HK-001b: post-edit read file_path from stdin",
21
+ HELPERS_GEN,
22
+ """" var file = process.env.TOOL_INPUT_file_path || args[0] || '';",""",
23
+ """" var file = (stdinData.tool_input && stdinData.tool_input.file_path) || args[0] || '';",""")
@@ -0,0 +1 @@
1
+ grep "stdinData" init/helpers-generator.js
@@ -0,0 +1,36 @@
1
+ # HK-002: MCP hook handlers are stubs that don't persist data
2
+
3
+ **Severity**: High
4
+ **GitHub**: [#1058](https://github.com/ruvnet/claude-flow/issues/1058)
5
+
6
+ ## Root Cause
7
+
8
+ Three MCP hook handlers in `hooks-tools.js` return success responses but **never persist any data**:
9
+
10
+ 1. **`hooksPostEdit`** (line 512) -- Returns `{recorded: true}` but has no database INSERT
11
+ 2. **`hooksPostCommand`** (line 568) -- Same, claims recorded but stores nothing
12
+ 3. **`hooksPostTask`** (line 886) -- Returns fake random duration and hardcoded pattern counts
13
+
14
+ The store function `getRealStoreFunction()` (line 23) already exists in the file and is used correctly by other handlers (`hooks_intelligence_trajectory-end`, `hooks_intelligence_pattern-store`). These three just never call it.
15
+
16
+ ## Fix
17
+
18
+ Patch each handler to call `getRealStoreFunction()` and persist to appropriate namespaces:
19
+ - `hooksPostEdit` -> namespace: `edits`
20
+ - `hooksPostCommand` -> namespace: `commands`
21
+ - `hooksPostTask` -> namespace: `tasks`
22
+
23
+ ## Impact Without Patch
24
+
25
+ - Edit patterns never stored -- can't learn from file edits
26
+ - Command history lost -- can't learn from command outcomes
27
+ - Task outcomes not tracked -- SONA learning has no data
28
+ - Misleading metrics -- statusline shows fake pattern counts
29
+
30
+ ## Files Patched
31
+
32
+ - `mcp-tools/hooks-tools.js`
33
+
34
+ ## Ops
35
+
36
+ 3 ops in fix.py
@@ -0,0 +1,155 @@
1
+ # HK-002: MCP hook handlers are stubs that don't persist data
2
+ # GitHub: #1058
3
+ # Restored from deleted HK-001 (commit 95a6a23)
4
+
5
+ # HK-002a: hooksPostEdit - add persistence
6
+ patch("HK-002a: hooksPostEdit persistence",
7
+ MCP_HOOKS,
8
+ """ handler: async (params) => {
9
+ const filePath = params.filePath;
10
+ const success = params.success !== false;
11
+ return {
12
+ recorded: true,
13
+ filePath,
14
+ success,
15
+ timestamp: new Date().toISOString(),
16
+ learningUpdate: success ? 'pattern_reinforced' : 'pattern_adjusted',
17
+ };
18
+ },
19
+ };
20
+ export const hooksPreCommand""",
21
+ """ handler: async (params) => {
22
+ const filePath = params.filePath;
23
+ const success = params.success !== false;
24
+ const agent = params.agent || 'unknown';
25
+ const timestamp = new Date().toISOString();
26
+ const editId = `edit-${Date.now()}-${Math.random().toString(36).substring(7)}`;
27
+ // HK-002a: Actually persist the edit record
28
+ const storeFn = await getRealStoreFunction();
29
+ let storeResult = { success: false };
30
+ if (storeFn) {
31
+ try {
32
+ storeResult = await storeFn({
33
+ key: editId,
34
+ value: JSON.stringify({ filePath, success, agent, timestamp }),
35
+ namespace: 'edits',
36
+ generateEmbeddingFlag: true,
37
+ tags: [success ? 'success' : 'failure', 'edit', agent],
38
+ });
39
+ } catch (e) { storeResult = { success: false, error: String(e) }; }
40
+ }
41
+ return {
42
+ recorded: storeResult.success,
43
+ filePath,
44
+ success,
45
+ timestamp,
46
+ learningUpdate: success ? 'pattern_reinforced' : 'pattern_adjusted',
47
+ };
48
+ },
49
+ };
50
+ export const hooksPreCommand""")
51
+
52
+ # HK-002b: hooksPostCommand - add persistence
53
+ patch("HK-002b: hooksPostCommand persistence",
54
+ MCP_HOOKS,
55
+ """ handler: async (params) => {
56
+ const command = params.command;
57
+ const exitCode = params.exitCode || 0;
58
+ return {
59
+ recorded: true,
60
+ command,
61
+ exitCode,
62
+ success: exitCode === 0,
63
+ timestamp: new Date().toISOString(),
64
+ };
65
+ },
66
+ };
67
+ export const hooksRoute""",
68
+ """ handler: async (params) => {
69
+ const command = params.command;
70
+ const exitCode = params.exitCode || 0;
71
+ const success = exitCode === 0;
72
+ const timestamp = new Date().toISOString();
73
+ const cmdId = `cmd-${Date.now()}-${Math.random().toString(36).substring(7)}`;
74
+ // HK-002b: Actually persist the command record
75
+ const storeFn = await getRealStoreFunction();
76
+ let storeResult = { success: false };
77
+ if (storeFn) {
78
+ try {
79
+ storeResult = await storeFn({
80
+ key: cmdId,
81
+ value: JSON.stringify({ command, exitCode, success, timestamp }),
82
+ namespace: 'commands',
83
+ generateEmbeddingFlag: true,
84
+ tags: [success ? 'success' : 'failure', 'command'],
85
+ });
86
+ } catch (e) { storeResult = { success: false, error: String(e) }; }
87
+ }
88
+ return {
89
+ recorded: storeResult.success,
90
+ command,
91
+ exitCode,
92
+ success,
93
+ timestamp,
94
+ };
95
+ },
96
+ };
97
+ export const hooksRoute""")
98
+
99
+ # HK-002c: hooksPostTask - add persistence, remove fake random data
100
+ patch("HK-002c: hooksPostTask persistence",
101
+ MCP_HOOKS,
102
+ """ handler: async (params) => {
103
+ const taskId = params.taskId;
104
+ const success = params.success !== false;
105
+ const quality = params.quality || (success ? 0.85 : 0.3);
106
+ return {
107
+ taskId,
108
+ success,
109
+ duration: Math.floor(Math.random() * 300) + 60, // 1-6 minutes in seconds
110
+ learningUpdates: {
111
+ patternsUpdated: success ? 2 : 1,
112
+ newPatterns: success ? 1 : 0,
113
+ trajectoryId: `traj-${Date.now()}`,
114
+ },
115
+ quality,
116
+ timestamp: new Date().toISOString(),
117
+ };
118
+ },
119
+ };
120
+ // Explain hook""",
121
+ """ handler: async (params) => {
122
+ const taskId = params.taskId;
123
+ const success = params.success !== false;
124
+ const agent = params.agent || 'unknown';
125
+ const quality = params.quality || (success ? 0.85 : 0.3);
126
+ const timestamp = new Date().toISOString();
127
+ // HK-002c: Actually persist the task record
128
+ const storeFn = await getRealStoreFunction();
129
+ let storeResult = { success: false };
130
+ if (storeFn) {
131
+ try {
132
+ storeResult = await storeFn({
133
+ key: `task-${taskId}`,
134
+ value: JSON.stringify({ taskId, success, agent, quality, timestamp }),
135
+ namespace: 'tasks',
136
+ generateEmbeddingFlag: true,
137
+ tags: [success ? 'success' : 'failure', 'task', agent],
138
+ });
139
+ } catch (e) { storeResult = { success: false, error: String(e) }; }
140
+ }
141
+ return {
142
+ taskId,
143
+ success,
144
+ recorded: storeResult.success,
145
+ learningUpdates: {
146
+ patternsUpdated: storeResult.success ? 1 : 0,
147
+ newPatterns: storeResult.success ? 1 : 0,
148
+ trajectoryId: `task-${taskId}`,
149
+ },
150
+ quality,
151
+ timestamp,
152
+ };
153
+ },
154
+ };
155
+ // Explain hook""")
@@ -0,0 +1 @@
1
+ grep "HK-002a" mcp-tools/hooks-tools.js
@@ -0,0 +1,30 @@
1
+ # HK-003: hooks_metrics MCP handler returns hardcoded fake data
2
+
3
+ **Severity**: High
4
+ **GitHub**: [#1158](https://github.com/ruvnet/claude-flow/issues/1158)
5
+
6
+ ## Root Cause
7
+
8
+ The `hooksMetrics` handler in `mcp-tools/hooks-tools.js` returns a static
9
+ object literal with fake values (15 patterns, 87% routing accuracy, 128
10
+ commands executed). It never reads from any persistence layer.
11
+
12
+ This is the same class of defect as HK-002 (hook handlers returning fake
13
+ data without persisting), but for the metrics/dashboard endpoint.
14
+
15
+ ## Fix
16
+
17
+ Replace the hardcoded return with a function that:
18
+ 1. Reads `.swarm/sona-patterns.json` for pattern counts, confidence, and routing stats
19
+ 2. Reads `.ruvector/intelligence.json` for trajectory/command counts and success rates
20
+ 3. Computes actual metrics from the persisted data
21
+ 4. Falls back to zeros when files don't exist
22
+ 5. Preserves the static performance targets (those are design goals, not metrics)
23
+
24
+ ## Files Patched
25
+
26
+ - `mcp-tools/hooks-tools.js`
27
+
28
+ ## Ops
29
+
30
+ 1 op in fix.py
@@ -0,0 +1,82 @@
1
+ # HK-003: hooks_metrics MCP handler returns hardcoded fake data
2
+ # GitHub: #1158
3
+
4
+ patch("HK-003a: replace hardcoded metrics with real data reader",
5
+ MCP_HOOKS,
6
+ """ return {
7
+ period,
8
+ patterns: {
9
+ total: 15,
10
+ successful: 12,
11
+ failed: 3,
12
+ avgConfidence: 0.85,
13
+ },
14
+ agents: {
15
+ routingAccuracy: 0.87,
16
+ totalRoutes: 42,
17
+ topAgent: 'coder',
18
+ },
19
+ commands: {
20
+ totalExecuted: 128,
21
+ successRate: 0.94,
22
+ avgRiskScore: 0.15,
23
+ },
24
+ performance: {
25
+ flashAttention: '2.49x-7.47x speedup',
26
+ memoryReduction: '50-75% reduction',
27
+ searchImprovement: '150x-12,500x faster',
28
+ tokenReduction: '32.3% fewer tokens',
29
+ },
30
+ status: 'healthy',
31
+ lastUpdated: new Date().toISOString(),
32
+ };""",
33
+ """ // HK-003: read real metrics from persisted files instead of hardcoded values
34
+ const cwd = process.cwd();
35
+ let patterns = { total: 0, successful: 0, failed: 0, avgConfidence: 0 };
36
+ let agents = { routingAccuracy: 0, totalRoutes: 0, topAgent: 'none' };
37
+ let commands = { totalExecuted: 0, successRate: 0, avgRiskScore: 0 };
38
+ try {
39
+ const sonaPath = cwd + '/.swarm/sona-patterns.json';
40
+ if (existsSync(sonaPath)) {
41
+ const sona = JSON.parse(readFileSync(sonaPath, 'utf-8'));
42
+ const pats = Object.values(sona.patterns || {});
43
+ const successful = pats.filter(p => p.successCount > 0).length;
44
+ patterns = {
45
+ total: pats.length,
46
+ successful,
47
+ failed: pats.filter(p => p.failureCount > 0).length,
48
+ avgConfidence: pats.length > 0 ? pats.reduce((s, p) => s + (p.confidence || 0), 0) / pats.length : 0,
49
+ };
50
+ agents = {
51
+ routingAccuracy: (sona.stats || {}).successfulRoutings > 0 ? sona.stats.successfulRoutings / ((sona.stats.successfulRoutings || 0) + (sona.stats.failedRoutings || 0)) : 0,
52
+ totalRoutes: ((sona.stats || {}).successfulRoutings || 0) + ((sona.stats || {}).failedRoutings || 0),
53
+ topAgent: pats.length > 0 ? pats.sort((a, b) => (b.successCount || 0) - (a.successCount || 0))[0].agent || 'none' : 'none',
54
+ };
55
+ }
56
+ } catch {}
57
+ try {
58
+ const rvPath = cwd + '/.ruvector/intelligence.json';
59
+ if (existsSync(rvPath)) {
60
+ const rv = JSON.parse(readFileSync(rvPath, 'utf-8'));
61
+ const s = rv.stats || {};
62
+ commands = {
63
+ totalExecuted: (s.session_count || 0) + (rv.trajectories || []).length,
64
+ successRate: (rv.trajectories || []).length > 0 ? (rv.trajectories || []).filter(t => t.success).length / (rv.trajectories || []).length : 0,
65
+ avgRiskScore: 0.15,
66
+ };
67
+ }
68
+ } catch {}
69
+ return {
70
+ period,
71
+ patterns,
72
+ agents,
73
+ commands,
74
+ performance: {
75
+ flashAttention: '2.49x-7.47x speedup',
76
+ memoryReduction: '50-75% reduction',
77
+ searchImprovement: '150x-12,500x faster',
78
+ tokenReduction: '32.3% fewer tokens',
79
+ },
80
+ status: 'healthy',
81
+ lastUpdated: new Date().toISOString(),
82
+ };""")
@@ -0,0 +1 @@
1
+ grep "HK-003" mcp-tools/hooks-tools.js
@@ -0,0 +1,11 @@
1
+ # HK-004: hooks_session-start ignores daemon.autoStart from settings.json
2
+ **Severity**: High
3
+ **GitHub**: [#1175](https://github.com/ruvnet/claude-flow/issues/1175)
4
+ ## Root Cause
5
+ `hooks_session-start` handler in `hooks-tools.js` (line ~1216) determines daemon auto-start solely from the MCP call parameter: `const shouldStartDaemon = params.startDaemon !== false;`. It never reads `claudeFlow.daemon.autoStart` from `.claude/settings.json`. Setting `autoStart: false` in settings.json has no effect — the daemon always starts on session-start.
6
+ ## Fix
7
+ Wrap the `shouldStartDaemon` assignment in an IIFE that first checks the MCP parameter, then reads settings.json. If `claudeFlow.daemon.autoStart === false`, returns false. Falls back to true on any read/parse error.
8
+ ## Files Patched
9
+ - mcp-tools/hooks-tools.js
10
+ ## Ops
11
+ 1 op in fix.py
@@ -0,0 +1,14 @@
1
+ # HK-004: hooks_session-start ignores daemon.autoStart from settings.json
2
+ # GitHub: #1175
3
+ patch("HK-004: respect daemon autoStart setting",
4
+ MCP_HOOKS,
5
+ " const shouldStartDaemon = params.startDaemon !== false;",
6
+ """ const shouldStartDaemon = (() => {
7
+ if (params.startDaemon === false) return false;
8
+ try {
9
+ const sp = join(process.cwd(), '.claude', 'settings.json');
10
+ const s = JSON.parse(readFileSync(sp, 'utf-8'));
11
+ if (s?.claudeFlow?.daemon?.autoStart === false) return false;
12
+ } catch { /* no settings or unreadable — default to true */ }
13
+ return true;
14
+ })();""")
@@ -0,0 +1 @@
1
+ grep "claudeFlow?.daemon?.autoStart" mcp-tools/hooks-tools.js
@@ -0,0 +1,11 @@
1
+ # HK-005: Multiple MCP servers start independent in-process daemons
2
+ **Severity**: Critical
3
+ **GitHub**: [#1171](https://github.com/ruvnet/claude-flow/issues/1171)
4
+ ## Root Cause
5
+ `hooks_session-start` calls `startDaemon()` (worker-daemon.js) which creates an in-process `WorkerDaemon` singleton per Node.js process. Each MCP server is a separate process with its own singleton — no cross-process coordination. The CLI background daemon path (`daemon.js`) has PID-file coordination but the MCP hook path bypasses it entirely. Result: N MCP servers per project = N daemon instances = N × 6 workers.
6
+ ## Fix
7
+ Add PID-file guard to the MCP hook path using `.claude-flow/daemon.pid` (same file the CLI path uses). Before calling `startDaemon()`, check PID file: if a different process owns it and is alive, skip (reuse). If the PID is our own process or stale, proceed and overwrite. No cleanup on session-end — stale PIDs self-heal via `kill(pid, 0)` on next start. The PID file becomes a universal one-daemon-per-project lock across both MCP and CLI paths.
8
+ ## Files Patched
9
+ - mcp-tools/hooks-tools.js
10
+ ## Ops
11
+ 2 ops in fix.py
@@ -0,0 +1,53 @@
1
+ # HK-005: Multiple MCP servers start independent in-process daemons
2
+ # No cross-process coordination on the hooks_session-start path.
3
+ # GitHub: #1171
4
+
5
+ # Op 1: Add PID-file guard before startDaemon()
6
+ patch("HK-005a: cross-process daemon PID guard",
7
+ MCP_HOOKS,
8
+ """ // Auto-start daemon if enabled
9
+ let daemonStatus = { started: false };
10
+ if (shouldStartDaemon) {
11
+ try {
12
+ // Dynamic import to avoid circular dependencies
13
+ const { startDaemon } = await import('../services/worker-daemon.js');
14
+ const daemon = await startDaemon(process.cwd());""",
15
+ """ // Auto-start daemon if enabled
16
+ let daemonStatus = { started: false };
17
+ if (shouldStartDaemon) {
18
+ try {
19
+ // HK-005: PID-file guard — one daemon per project across processes
20
+ const _pidDir = join(process.cwd(), '.claude-flow');
21
+ const _pidPath = join(_pidDir, 'daemon.pid');
22
+ let _skipDaemon = false;
23
+ try {
24
+ const _xPid = parseInt(readFileSync(_pidPath, 'utf-8').trim(), 10);
25
+ if (!isNaN(_xPid) && _xPid !== process.pid) {
26
+ try { process.kill(_xPid, 0); _skipDaemon = true; daemonStatus = { started: true, pid: _xPid, reused: true }; }
27
+ catch { /* stale PID from dead process — proceed */ }
28
+ }
29
+ } catch { /* no PID file — proceed */ }
30
+ if (!_skipDaemon) {
31
+ // Dynamic import to avoid circular dependencies
32
+ const { startDaemon } = await import('../services/worker-daemon.js');
33
+ const daemon = await startDaemon(process.cwd());""")
34
+
35
+ # Op 2: Write PID after successful start + close guard block
36
+ patch("HK-005b: write PID after daemon start",
37
+ MCP_HOOKS,
38
+ """ const status = daemon.getStatus();
39
+ daemonStatus = {
40
+ started: true,
41
+ pid: status.pid,
42
+ };""",
43
+ """ const status = daemon.getStatus();
44
+ // HK-005: Write PID so other processes detect this daemon
45
+ try {
46
+ if (!existsSync(_pidDir)) { mkdirSync(_pidDir, { recursive: true }); }
47
+ writeFileSync(_pidPath, String(status.pid || process.pid));
48
+ } catch { /* best-effort */ }
49
+ daemonStatus = {
50
+ started: true,
51
+ pid: status.pid,
52
+ };
53
+ } // end HK-005 guard""")
@@ -0,0 +1,2 @@
1
+ grep "HK-005: PID-file guard" mcp-tools/hooks-tools.js
2
+ grep "HK-005: Write PID" mcp-tools/hooks-tools.js
@@ -0,0 +1,11 @@
1
+ # HW-001: Headless workers hang — stdin pipe never closed
2
+ **Severity**: Critical
3
+ **GitHub**: [#1111](https://github.com/ruvnet/claude-flow/issues/1111)
4
+ ## Root Cause
5
+ `claude --print` is spawned with `stdio: ['pipe', 'pipe', 'pipe']`. The prompt is passed as a CLI argument, so stdin is never written to or closed. The child process hangs waiting for stdin EOF.
6
+ ## Fix
7
+ Change stdin to `'ignore'` since it's unused.
8
+ ## Files Patched
9
+ - services/headless-worker-executor.js
10
+ ## Ops
11
+ 1 op in fix.py
@@ -0,0 +1,6 @@
1
+ # HW-001: Headless workers hang — stdin pipe never closed
2
+ # GitHub: #1111
3
+ patch("1: stdin pipe",
4
+ HWE,
5
+ "stdio: ['pipe', 'pipe', 'pipe']",
6
+ "stdio: ['ignore', 'pipe', 'pipe']")
@@ -0,0 +1 @@
1
+ grep "'ignore', 'pipe', 'pipe'" services/headless-worker-executor.js
@@ -0,0 +1,11 @@
1
+ # HW-002: Headless failures silently swallowed as success
2
+ **Severity**: High
3
+ **GitHub**: [#1112](https://github.com/ruvnet/claude-flow/issues/1112)
4
+ ## Root Cause
5
+ `executeWorker()` hardcodes `success: true` for any non-throwing return. When headless execution fails (returns `{success: false}`), `runWorkerLogic()` catches the error and falls through to local stubs that fabricate data. Failures are never surfaced.
6
+ ## Fix
7
+ Check `result.success` after headless execution. If false, throw with the error message instead of falling through.
8
+ ## Files Patched
9
+ - services/worker-daemon.js
10
+ ## Ops
11
+ 1 op in fix.py
@@ -0,0 +1,42 @@
1
+ # HW-002: Headless failures silently swallowed as success
2
+ # GitHub: #1112
3
+ patch("2: honest failures",
4
+ WD,
5
+ """ if (isHeadlessWorker(workerConfig.type) && this.headlessAvailable && this.headlessExecutor) {
6
+ try {
7
+ this.log('info', `Running ${workerConfig.type} in headless mode (Claude Code AI)`);
8
+ const result = await this.headlessExecutor.execute(workerConfig.type);
9
+ return {
10
+ mode: 'headless',
11
+ ...result,
12
+ };
13
+ }
14
+ catch (error) {
15
+ this.log('warn', `Headless execution failed for ${workerConfig.type}, falling back to local mode`);
16
+ this.emit('headless:fallback', {
17
+ type: workerConfig.type,
18
+ error: error instanceof Error ? error.message : String(error),
19
+ });
20
+ // Fall through to local execution
21
+ }
22
+ }""",
23
+ """ if (isHeadlessWorker(workerConfig.type) && this.headlessAvailable && this.headlessExecutor) {
24
+ let result;
25
+ try {
26
+ this.log('info', `Running ${workerConfig.type} in headless mode (Claude Code AI)`);
27
+ result = await this.headlessExecutor.execute(workerConfig.type);
28
+ }
29
+ catch (error) {
30
+ const errorMsg = error instanceof Error ? error.message : String(error);
31
+ this.log('warn', `Headless execution threw for ${workerConfig.type}: ${errorMsg}`);
32
+ this.emit('headless:fallback', { type: workerConfig.type, error: errorMsg });
33
+ throw error instanceof Error ? error : new Error(errorMsg);
34
+ }
35
+ if (result.success) {
36
+ return { mode: 'headless', ...result };
37
+ }
38
+ const errorMsg = result.error || 'Unknown headless failure';
39
+ this.log('warn', `Headless failed for ${workerConfig.type}: ${errorMsg}`);
40
+ this.emit('headless:fallback', { type: workerConfig.type, error: errorMsg });
41
+ throw new Error(`Headless execution failed for ${workerConfig.type}: ${errorMsg}`);
42
+ }""")
@@ -0,0 +1 @@
1
+ grep "result.success" services/worker-daemon.js
@@ -0,0 +1,11 @@
1
+ # HW-003: Worker scheduling intervals too aggressive + settings ignored
2
+ **Severity**: High
3
+ **GitHub**: [#1113](https://github.com/ruvnet/claude-flow/issues/1113)
4
+ ## Root Cause
5
+ `DEFAULT_WORKERS` uses pre-headless intervals (audit: 10m, optimize: 15m, testgaps: 20m). ADR-020 specifies longer intervals (30/60/60m) for headless workers that invoke Claude. Additionally, `daemon.schedules` from `.claude/settings.json` is never read — user-configured intervals are completely ignored.
6
+ ## Fix
7
+ (A) Align hardcoded intervals to ADR-020: audit 30m, optimize 60m, testgaps 60m. (B) After setting default workers, read `claudeFlow.daemon.schedules` from `.claude/settings.json` and merge user-configured intervals/enabled flags into matching workers. Supports string formats ("1h", "30m", "10s") and raw milliseconds.
8
+ ## Files Patched
9
+ - services/worker-daemon.js
10
+ ## Ops
11
+ 4 ops in fix.py