claude-evolve 1.3.39 → 1.3.40
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.
- package/README.md +40 -2
- package/bin/claude-evolve-analyze +34 -5
- package/bin/claude-evolve-cleanup +297 -0
- package/bin/claude-evolve-edit +293 -0
- package/bin/claude-evolve-ideate +6 -6
- package/bin/claude-evolve-main +35 -15
- package/bin/{claude-evolve-run-unified → claude-evolve-run} +135 -4
- package/bin/claude-evolve-status +220 -0
- package/bin/claude-evolve-worker +73 -11
- package/lib/config.sh +5 -3
- package/lib/csv-lock.sh +26 -4
- package/lib/csv_helper.py +1 -1
- package/package.json +1 -1
- package/templates/config.yaml +8 -7
- package/bin/claude-evolve-run-parallel.OLD +0 -389
- package/bin/claude-evolve-run.OLD +0 -662
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# Load configuration
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
# shellcheck source=../lib/config.sh
|
|
8
|
+
source "$SCRIPT_DIR/../lib/config.sh"
|
|
9
|
+
|
|
10
|
+
# Use CLAUDE_EVOLVE_CONFIG if set, otherwise default
|
|
11
|
+
if [[ -n ${CLAUDE_EVOLVE_CONFIG:-} ]]; then
|
|
12
|
+
load_config "$CLAUDE_EVOLVE_CONFIG"
|
|
13
|
+
else
|
|
14
|
+
load_config
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Function to show help
|
|
18
|
+
show_help() {
|
|
19
|
+
cat <<EOF
|
|
20
|
+
claude-evolve edit - Manage evolution candidate statuses by generation or status
|
|
21
|
+
|
|
22
|
+
USAGE:
|
|
23
|
+
claude-evolve edit <selector> <action>
|
|
24
|
+
|
|
25
|
+
SELECTORS:
|
|
26
|
+
gen01, gen02, etc. Target specific generation
|
|
27
|
+
all Target all generations
|
|
28
|
+
failed Target all candidates with failed status
|
|
29
|
+
complete Target all candidates with complete status
|
|
30
|
+
pending Target all candidates with pending status
|
|
31
|
+
running Target all candidates with running status
|
|
32
|
+
|
|
33
|
+
ACTIONS:
|
|
34
|
+
failed Mark candidates as failed (keeps scores)
|
|
35
|
+
complete Mark candidates as complete (keeps scores)
|
|
36
|
+
pending Mark candidates as pending (keeps scores)
|
|
37
|
+
reboot Reset completely (delete .py files, clear scores, set pending)
|
|
38
|
+
|
|
39
|
+
EXAMPLES:
|
|
40
|
+
claude-evolve edit gen03 failed # Mark all gen03 as failed
|
|
41
|
+
claude-evolve edit failed pending # Reset all failed candidates to pending
|
|
42
|
+
claude-evolve edit complete failed # Mark all complete as failed for re-run
|
|
43
|
+
claude-evolve edit all pending # Mark everything as pending for re-run
|
|
44
|
+
claude-evolve edit gen02 reboot # Full reset of gen02 (delete files + clear data)
|
|
45
|
+
|
|
46
|
+
DESCRIPTION:
|
|
47
|
+
This command helps manage evolution runs when you need to re-evaluate candidates.
|
|
48
|
+
Use status selectors (failed, complete, etc.) to bulk-change candidates by status.
|
|
49
|
+
Use 'reboot' for complete reset including file deletion.
|
|
50
|
+
EOF
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Parse arguments
|
|
54
|
+
if [[ $# -ne 2 ]]; then
|
|
55
|
+
show_help
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
SELECTOR="$1"
|
|
60
|
+
ACTION="$2"
|
|
61
|
+
|
|
62
|
+
# Validate configuration
|
|
63
|
+
if ! validate_config; then
|
|
64
|
+
echo "[ERROR] Configuration validation failed" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Validate selector format
|
|
69
|
+
if [[ "$SELECTOR" != "all" && ! "$SELECTOR" =~ ^gen[0-9]+$ && "$SELECTOR" != "failed" && "$SELECTOR" != "complete" && "$SELECTOR" != "pending" && "$SELECTOR" != "running" ]]; then
|
|
70
|
+
echo "[ERROR] Selector must be 'all', 'genXX' (e.g., gen01), or status ('failed', 'complete', 'pending', 'running')" >&2
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Validate action
|
|
75
|
+
case "$ACTION" in
|
|
76
|
+
failed|complete|pending|reboot) ;;
|
|
77
|
+
*)
|
|
78
|
+
echo "[ERROR] Action must be one of: failed, complete, pending, reboot" >&2
|
|
79
|
+
exit 1
|
|
80
|
+
;;
|
|
81
|
+
esac
|
|
82
|
+
|
|
83
|
+
# Check if CSV exists
|
|
84
|
+
if [[ ! -f "$FULL_CSV_PATH" ]]; then
|
|
85
|
+
echo "[ERROR] Evolution CSV not found: $FULL_CSV_PATH" >&2
|
|
86
|
+
echo "Run 'claude-evolve setup' first or navigate to the correct directory" >&2
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Function to update CSV status for specific selector
|
|
91
|
+
update_candidates_status() {
|
|
92
|
+
local selector="$1"
|
|
93
|
+
local new_status="$2"
|
|
94
|
+
local clear_scores="$3"
|
|
95
|
+
|
|
96
|
+
echo "[INFO] Updating candidates matching '$selector' to status: $new_status"
|
|
97
|
+
|
|
98
|
+
# Use Python to safely edit the CSV
|
|
99
|
+
"$PYTHON_CMD" -c "
|
|
100
|
+
import csv
|
|
101
|
+
import sys
|
|
102
|
+
import os
|
|
103
|
+
|
|
104
|
+
csv_file = '$FULL_CSV_PATH'
|
|
105
|
+
selector = '$selector'
|
|
106
|
+
new_status = '$new_status'
|
|
107
|
+
clear_scores = '$clear_scores' == 'true'
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
# Read CSV
|
|
111
|
+
with open(csv_file, 'r') as f:
|
|
112
|
+
reader = csv.reader(f)
|
|
113
|
+
rows = list(reader)
|
|
114
|
+
|
|
115
|
+
if not rows:
|
|
116
|
+
print('[ERROR] CSV is empty')
|
|
117
|
+
sys.exit(1)
|
|
118
|
+
|
|
119
|
+
header = rows[0]
|
|
120
|
+
updated_count = 0
|
|
121
|
+
|
|
122
|
+
# Update matching rows
|
|
123
|
+
for i in range(1, len(rows)):
|
|
124
|
+
row = rows[i]
|
|
125
|
+
if len(row) < 1:
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
candidate_id = row[0]
|
|
129
|
+
current_status = row[4] if len(row) > 4 else ''
|
|
130
|
+
|
|
131
|
+
# Check if this row matches selector
|
|
132
|
+
matches = False
|
|
133
|
+
if selector == 'all':
|
|
134
|
+
matches = True
|
|
135
|
+
elif selector.startswith('gen') and '-' in candidate_id:
|
|
136
|
+
# Generation selector (e.g., gen01, gen02)
|
|
137
|
+
matches = candidate_id.startswith(selector + '-')
|
|
138
|
+
elif selector in ['failed', 'complete', 'pending', 'running']:
|
|
139
|
+
# Status selector
|
|
140
|
+
if selector == 'pending':
|
|
141
|
+
matches = current_status == '' or current_status == 'pending'
|
|
142
|
+
else:
|
|
143
|
+
matches = current_status == selector
|
|
144
|
+
|
|
145
|
+
if matches:
|
|
146
|
+
if clear_scores:
|
|
147
|
+
# Reboot: clear everything after description (keep id, basedOnId, description)
|
|
148
|
+
if len(row) >= 3:
|
|
149
|
+
rows[i] = [row[0], row[1], row[2], '', ''] # id, basedOnId, description, empty performance, empty status
|
|
150
|
+
updated_count += 1
|
|
151
|
+
else:
|
|
152
|
+
# Just update status (preserve other fields)
|
|
153
|
+
# Ensure row has at least 5 fields
|
|
154
|
+
while len(row) < 5:
|
|
155
|
+
row.append('')
|
|
156
|
+
row[4] = new_status # Update status field
|
|
157
|
+
updated_count += 1
|
|
158
|
+
|
|
159
|
+
# Write back to CSV
|
|
160
|
+
with open(csv_file + '.tmp', 'w', newline='') as f:
|
|
161
|
+
writer = csv.writer(f)
|
|
162
|
+
writer.writerows(rows)
|
|
163
|
+
|
|
164
|
+
# Atomic replace
|
|
165
|
+
os.rename(csv_file + '.tmp', csv_file)
|
|
166
|
+
|
|
167
|
+
print(f'[INFO] Updated {updated_count} candidates')
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
print(f'[ERROR] Failed to update CSV: {e}')
|
|
171
|
+
sys.exit(1)
|
|
172
|
+
"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# Function to delete evolution files for selector
|
|
176
|
+
delete_evolution_files() {
|
|
177
|
+
local selector="$1"
|
|
178
|
+
|
|
179
|
+
if [[ ! -d "$FULL_EVOLUTION_DIR" ]]; then
|
|
180
|
+
echo "[WARN] Evolution directory not found: $FULL_EVOLUTION_DIR"
|
|
181
|
+
return
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
local deleted_count=0
|
|
185
|
+
|
|
186
|
+
if [[ "$selector" == "all" ]]; then
|
|
187
|
+
echo "[INFO] Deleting all evolution_*.py files..."
|
|
188
|
+
for file in "$FULL_EVOLUTION_DIR"/evolution_*.py; do
|
|
189
|
+
if [[ -f "$file" ]]; then
|
|
190
|
+
rm "$file"
|
|
191
|
+
((deleted_count++))
|
|
192
|
+
echo "[INFO] Deleted: $(basename "$file")"
|
|
193
|
+
fi
|
|
194
|
+
done
|
|
195
|
+
elif [[ "$selector" =~ ^gen[0-9]+$ ]]; then
|
|
196
|
+
echo "[INFO] Deleting evolution files for $selector..."
|
|
197
|
+
for file in "$FULL_EVOLUTION_DIR"/evolution_${selector}-*.py; do
|
|
198
|
+
if [[ -f "$file" ]]; then
|
|
199
|
+
rm "$file"
|
|
200
|
+
((deleted_count++))
|
|
201
|
+
echo "[INFO] Deleted: $(basename "$file")"
|
|
202
|
+
fi
|
|
203
|
+
done
|
|
204
|
+
else
|
|
205
|
+
# Status-based selector - need to query CSV for candidate IDs
|
|
206
|
+
echo "[INFO] Finding files to delete for status '$selector'..."
|
|
207
|
+
|
|
208
|
+
# Use Python to get list of candidate IDs matching the status
|
|
209
|
+
local candidates_to_delete
|
|
210
|
+
candidates_to_delete=$("$PYTHON_CMD" -c "
|
|
211
|
+
import csv
|
|
212
|
+
import sys
|
|
213
|
+
|
|
214
|
+
csv_file = '$FULL_CSV_PATH'
|
|
215
|
+
selector = '$selector'
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
with open(csv_file, 'r') as f:
|
|
219
|
+
reader = csv.reader(f)
|
|
220
|
+
next(reader) # Skip header
|
|
221
|
+
|
|
222
|
+
candidates = []
|
|
223
|
+
for row in reader:
|
|
224
|
+
if len(row) < 1:
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
candidate_id = row[0]
|
|
228
|
+
current_status = row[4] if len(row) > 4 else ''
|
|
229
|
+
|
|
230
|
+
# Check if matches status selector
|
|
231
|
+
matches = False
|
|
232
|
+
if selector == 'pending':
|
|
233
|
+
matches = current_status == '' or current_status == 'pending'
|
|
234
|
+
else:
|
|
235
|
+
matches = current_status == selector
|
|
236
|
+
|
|
237
|
+
if matches:
|
|
238
|
+
candidates.append(candidate_id)
|
|
239
|
+
|
|
240
|
+
print(' '.join(candidates))
|
|
241
|
+
|
|
242
|
+
except Exception as e:
|
|
243
|
+
print('', file=sys.stderr) # Empty output on error
|
|
244
|
+
sys.exit(1)
|
|
245
|
+
")
|
|
246
|
+
|
|
247
|
+
if [[ -n "$candidates_to_delete" ]]; then
|
|
248
|
+
for candidate_id in $candidates_to_delete; do
|
|
249
|
+
# Determine file format
|
|
250
|
+
if [[ "$candidate_id" =~ ^[0-9]+$ ]]; then
|
|
251
|
+
file="$FULL_EVOLUTION_DIR/evolution_id${candidate_id}.py"
|
|
252
|
+
else
|
|
253
|
+
file="$FULL_EVOLUTION_DIR/evolution_${candidate_id}.py"
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
if [[ -f "$file" ]]; then
|
|
257
|
+
rm "$file"
|
|
258
|
+
((deleted_count++))
|
|
259
|
+
echo "[INFO] Deleted: $(basename "$file")"
|
|
260
|
+
fi
|
|
261
|
+
done
|
|
262
|
+
fi
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
echo "[INFO] Deleted $deleted_count evolution files"
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# Main execution
|
|
269
|
+
echo "[INFO] Processing '$SELECTOR' with action: $ACTION"
|
|
270
|
+
|
|
271
|
+
case "$ACTION" in
|
|
272
|
+
failed)
|
|
273
|
+
update_candidates_status "$SELECTOR" "failed" "false"
|
|
274
|
+
;;
|
|
275
|
+
complete)
|
|
276
|
+
update_candidates_status "$SELECTOR" "complete" "false"
|
|
277
|
+
;;
|
|
278
|
+
pending)
|
|
279
|
+
update_candidates_status "$SELECTOR" "" "false" # Empty status means pending
|
|
280
|
+
;;
|
|
281
|
+
reboot)
|
|
282
|
+
echo "[INFO] Performing full reboot of '$SELECTOR'..."
|
|
283
|
+
delete_evolution_files "$SELECTOR"
|
|
284
|
+
update_candidates_status "$SELECTOR" "" "true" # Clear scores and set pending
|
|
285
|
+
echo "[INFO] Reboot complete: files deleted, scores cleared, status set to pending"
|
|
286
|
+
;;
|
|
287
|
+
esac
|
|
288
|
+
|
|
289
|
+
echo "[INFO] Edit operation complete"
|
|
290
|
+
|
|
291
|
+
# Call status command to show current state
|
|
292
|
+
echo ""
|
|
293
|
+
"$SCRIPT_DIR/claude-evolve-status" --brief
|
package/bin/claude-evolve-ideate
CHANGED
|
@@ -30,7 +30,7 @@ get_model_for_generation() {
|
|
|
30
30
|
if (( gen_num % 2 == 1 )); then
|
|
31
31
|
echo "opus" # Odd generations: use Opus for exploration
|
|
32
32
|
else
|
|
33
|
-
echo "o3
|
|
33
|
+
echo "o3" # Even generations: use o3 for refinement
|
|
34
34
|
fi
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -46,19 +46,19 @@ call_ai_with_limit_check() {
|
|
|
46
46
|
echo "[INFO] Generation $generation: Using $preferred_model" >&2
|
|
47
47
|
|
|
48
48
|
# Try preferred model first
|
|
49
|
-
if [[ "$preferred_model" == "o3
|
|
50
|
-
echo "[INFO] Using codex o3
|
|
49
|
+
if [[ "$preferred_model" == "o3" ]] && command -v codex >/dev/null 2>&1; then
|
|
50
|
+
echo "[INFO] Using codex o3 for ideation" >&2
|
|
51
51
|
|
|
52
|
-
# Call codex with o3
|
|
52
|
+
# Call codex with o3 model using -q flag and --full-auto
|
|
53
53
|
local ai_output
|
|
54
|
-
ai_output=$(codex -m o3
|
|
54
|
+
ai_output=$(codex -m o3 --full-auto -q "$prompt" 2>&1)
|
|
55
55
|
local ai_exit_code=$?
|
|
56
56
|
|
|
57
57
|
if [[ $ai_exit_code -eq 0 ]]; then
|
|
58
58
|
echo "$ai_output"
|
|
59
59
|
return 0
|
|
60
60
|
else
|
|
61
|
-
echo "[WARN] Codex o3
|
|
61
|
+
echo "[WARN] Codex o3 failed, falling back to Claude Opus" >&2
|
|
62
62
|
preferred_model="opus"
|
|
63
63
|
fi
|
|
64
64
|
fi
|
package/bin/claude-evolve-main
CHANGED
|
@@ -58,11 +58,15 @@ USAGE:
|
|
|
58
58
|
claude-evolve [--config=PATH] [COMMAND] [OPTIONS]
|
|
59
59
|
|
|
60
60
|
COMMANDS:
|
|
61
|
-
setup
|
|
62
|
-
ideate
|
|
63
|
-
run
|
|
64
|
-
analyze
|
|
65
|
-
|
|
61
|
+
setup Initialize evolution workspace
|
|
62
|
+
ideate Generate new algorithm ideas
|
|
63
|
+
run Execute evolution candidates
|
|
64
|
+
analyze Analyze evolution results
|
|
65
|
+
edit Manage candidate statuses by generation
|
|
66
|
+
status Show evolution progress and current leader
|
|
67
|
+
cleanup Clean up unchanged algorithms and descendants
|
|
68
|
+
cleanup-duplicates Alias for cleanup (deprecated)
|
|
69
|
+
help Show this help message
|
|
66
70
|
|
|
67
71
|
GLOBAL OPTIONS:
|
|
68
72
|
--config=PATH Use alternate config file (default: evolution/config.yaml)
|
|
@@ -91,9 +95,11 @@ show_menu() {
|
|
|
91
95
|
echo " 2) ideate - Generate new algorithm ideas"
|
|
92
96
|
echo " 3) run - Execute evolution candidates"
|
|
93
97
|
echo " 4) analyze - Analyze evolution results"
|
|
94
|
-
echo " 5)
|
|
95
|
-
echo " 6)
|
|
96
|
-
echo " 7)
|
|
98
|
+
echo " 5) edit - Manage candidate statuses by generation"
|
|
99
|
+
echo " 6) status - Show evolution progress and current leader"
|
|
100
|
+
echo " 7) config - Manage configuration settings"
|
|
101
|
+
echo " 8) help - Show help message"
|
|
102
|
+
echo " 9) exit - Exit"
|
|
97
103
|
echo
|
|
98
104
|
|
|
99
105
|
# Show workspace status
|
|
@@ -137,21 +143,23 @@ check_for_updates
|
|
|
137
143
|
# Main logic
|
|
138
144
|
if [[ $# -eq 0 ]]; then
|
|
139
145
|
show_menu
|
|
140
|
-
read -r -p "Enter your choice (1-
|
|
146
|
+
read -r -p "Enter your choice (1-9): " choice
|
|
141
147
|
|
|
142
148
|
case $choice in
|
|
143
149
|
1) exec "$SCRIPT_DIR/claude-evolve-setup" ;;
|
|
144
150
|
2) exec "$SCRIPT_DIR/claude-evolve-ideate" ;;
|
|
145
|
-
3) exec "$SCRIPT_DIR/claude-evolve-run
|
|
151
|
+
3) exec "$SCRIPT_DIR/claude-evolve-run" ;;
|
|
146
152
|
4) exec "$SCRIPT_DIR/claude-evolve-analyze" ;;
|
|
147
|
-
5) exec "$SCRIPT_DIR/claude-evolve-
|
|
148
|
-
6)
|
|
149
|
-
7)
|
|
153
|
+
5) exec "$SCRIPT_DIR/claude-evolve-edit" ;;
|
|
154
|
+
6) exec "$SCRIPT_DIR/claude-evolve-status" ;;
|
|
155
|
+
7) exec "$SCRIPT_DIR/claude-evolve-config" ;;
|
|
156
|
+
8) show_help ;;
|
|
157
|
+
9)
|
|
150
158
|
echo "Goodbye!"
|
|
151
159
|
exit 0
|
|
152
160
|
;;
|
|
153
161
|
*)
|
|
154
|
-
echo -e "${RED}Invalid choice. Please select 1-
|
|
162
|
+
echo -e "${RED}Invalid choice. Please select 1-9.${NC}"
|
|
155
163
|
exit 1
|
|
156
164
|
;;
|
|
157
165
|
esac
|
|
@@ -174,12 +182,24 @@ ideate)
|
|
|
174
182
|
;;
|
|
175
183
|
run)
|
|
176
184
|
shift
|
|
177
|
-
exec "$SCRIPT_DIR/claude-evolve-run
|
|
185
|
+
exec "$SCRIPT_DIR/claude-evolve-run" "$@"
|
|
178
186
|
;;
|
|
179
187
|
analyze)
|
|
180
188
|
shift
|
|
181
189
|
exec "$SCRIPT_DIR/claude-evolve-analyze" "$@"
|
|
182
190
|
;;
|
|
191
|
+
edit)
|
|
192
|
+
shift
|
|
193
|
+
exec "$SCRIPT_DIR/claude-evolve-edit" "$@"
|
|
194
|
+
;;
|
|
195
|
+
status)
|
|
196
|
+
shift
|
|
197
|
+
exec "$SCRIPT_DIR/claude-evolve-status" "$@"
|
|
198
|
+
;;
|
|
199
|
+
cleanup-duplicates|cleanup)
|
|
200
|
+
shift
|
|
201
|
+
exec "$SCRIPT_DIR/claude-evolve-cleanup" "$@"
|
|
202
|
+
;;
|
|
183
203
|
config)
|
|
184
204
|
shift
|
|
185
205
|
exec "$SCRIPT_DIR/claude-evolve-config" "$@"
|
|
@@ -236,12 +236,32 @@ cleanup_workers() {
|
|
|
236
236
|
# Worker finished
|
|
237
237
|
if wait "$pid" 2>/dev/null; then
|
|
238
238
|
echo "[DISPATCHER] Worker $pid completed successfully"
|
|
239
|
+
consecutive_failures=0 # Reset counter on success
|
|
239
240
|
else
|
|
240
241
|
local exit_code=$?
|
|
241
242
|
if [[ $exit_code -eq 2 ]]; then
|
|
242
243
|
echo "[DISPATCHER] Worker $pid hit rate limit, will retry later"
|
|
244
|
+
# Rate limits don't count as consecutive failures
|
|
243
245
|
else
|
|
244
246
|
echo "[DISPATCHER] Worker $pid failed with exit code $exit_code"
|
|
247
|
+
((consecutive_failures++))
|
|
248
|
+
|
|
249
|
+
# Check if we've hit the failure limit
|
|
250
|
+
if [[ $consecutive_failures -ge $MAX_CONSECUTIVE_FAILURES ]]; then
|
|
251
|
+
echo "" >&2
|
|
252
|
+
echo "🚨🚨🚨 EVOLUTION STOPPED: TOO MANY FAILURES 🚨🚨🚨" >&2
|
|
253
|
+
echo "ERROR: $consecutive_failures consecutive worker failures detected" >&2
|
|
254
|
+
echo "ERROR: This indicates a systemic problem (Claude API issues, evaluator bugs, etc.)" >&2
|
|
255
|
+
echo "ERROR: Check logs and fix issues before restarting evolution" >&2
|
|
256
|
+
echo "🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨" >&2
|
|
257
|
+
echo "" >&2
|
|
258
|
+
|
|
259
|
+
# Shutdown all workers and exit
|
|
260
|
+
shutdown_workers
|
|
261
|
+
exit 1
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
echo "[DISPATCHER] Consecutive failures: $consecutive_failures/$MAX_CONSECUTIVE_FAILURES"
|
|
245
265
|
fi
|
|
246
266
|
fi
|
|
247
267
|
fi
|
|
@@ -269,9 +289,26 @@ get_csv_stats() {
|
|
|
269
289
|
total_rows=$(wc -l < "$csv_path" | tr -d '[:space:]')
|
|
270
290
|
complete_count=$(grep ',complete' "$csv_path" 2>/dev/null | wc -l | tr -d '[:space:]')
|
|
271
291
|
|
|
272
|
-
# Count pending
|
|
273
|
-
#
|
|
274
|
-
pending_count=$(
|
|
292
|
+
# Count pending using same logic as find_next_pending_with_lock
|
|
293
|
+
# This includes rows with <5 fields AND rows with empty/pending status
|
|
294
|
+
pending_count=$("$PYTHON_CMD" -c "
|
|
295
|
+
import csv
|
|
296
|
+
import sys
|
|
297
|
+
|
|
298
|
+
pending_count = 0
|
|
299
|
+
with open('$csv_path', 'r') as f:
|
|
300
|
+
reader = csv.reader(f)
|
|
301
|
+
rows = list(reader)
|
|
302
|
+
|
|
303
|
+
for i in range(1, len(rows)):
|
|
304
|
+
# Same logic as find_next_pending_with_lock
|
|
305
|
+
if len(rows[i]) < 5:
|
|
306
|
+
pending_count += 1
|
|
307
|
+
elif len(rows[i]) >= 5 and (rows[i][4] == 'pending' or rows[i][4] == ''):
|
|
308
|
+
pending_count += 1
|
|
309
|
+
|
|
310
|
+
print(pending_count)
|
|
311
|
+
")
|
|
275
312
|
|
|
276
313
|
echo "$total_rows $complete_count $pending_count"
|
|
277
314
|
}
|
|
@@ -279,6 +316,100 @@ get_csv_stats() {
|
|
|
279
316
|
echo "[DISPATCHER] Starting unified evolution engine"
|
|
280
317
|
echo "[DISPATCHER] Configuration: max_workers=$MAX_WORKERS, timeout=${timeout_seconds:-none}"
|
|
281
318
|
|
|
319
|
+
# Validate CSV and clean up stuck statuses
|
|
320
|
+
if [[ -f "$FULL_CSV_PATH" ]]; then
|
|
321
|
+
echo "[DISPATCHER] Validating CSV and cleaning up..."
|
|
322
|
+
if ! "$PYTHON_CMD" -c "
|
|
323
|
+
import csv
|
|
324
|
+
import sys
|
|
325
|
+
|
|
326
|
+
csv_file = '$FULL_CSV_PATH'
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
# Read CSV - let Python's csv module handle all the complexity
|
|
330
|
+
with open(csv_file, 'r') as f:
|
|
331
|
+
reader = csv.reader(f)
|
|
332
|
+
rows = list(reader)
|
|
333
|
+
|
|
334
|
+
if not rows:
|
|
335
|
+
print('[ERROR] CSV is empty')
|
|
336
|
+
sys.exit(1)
|
|
337
|
+
|
|
338
|
+
# Basic sanity checks
|
|
339
|
+
header = rows[0]
|
|
340
|
+
num_fields = len(header)
|
|
341
|
+
|
|
342
|
+
if len(rows) == 1:
|
|
343
|
+
print('[INFO] CSV has no data rows (only header)')
|
|
344
|
+
|
|
345
|
+
# Clean up any stuck 'running' statuses
|
|
346
|
+
changed = 0
|
|
347
|
+
for i in range(1, len(rows)):
|
|
348
|
+
if len(rows[i]) > 4 and rows[i][4] == 'running':
|
|
349
|
+
rows[i][4] = ''
|
|
350
|
+
changed += 1
|
|
351
|
+
|
|
352
|
+
if changed > 0:
|
|
353
|
+
# Write back
|
|
354
|
+
with open(csv_file + '.tmp', 'w', newline='') as f:
|
|
355
|
+
writer = csv.writer(f)
|
|
356
|
+
writer.writerows(rows)
|
|
357
|
+
import os
|
|
358
|
+
os.rename(csv_file + '.tmp', csv_file)
|
|
359
|
+
print(f'[INFO] Reset {changed} stuck running candidates to pending')
|
|
360
|
+
|
|
361
|
+
# Count pending candidates
|
|
362
|
+
pending = 0
|
|
363
|
+
for i in range(1, len(rows)):
|
|
364
|
+
# Row with < 5 fields or empty/pending status in field 5
|
|
365
|
+
if len(rows[i]) < 5 or (len(rows[i]) >= 5 and rows[i][4] in ['', 'pending']):
|
|
366
|
+
pending += 1
|
|
367
|
+
|
|
368
|
+
print(f'[INFO] CSV loaded: {len(rows)-1} total candidates, {pending} pending')
|
|
369
|
+
|
|
370
|
+
except csv.Error as e:
|
|
371
|
+
print(f'[ERROR] CSV parsing error: {e}')
|
|
372
|
+
print('[ERROR] The CSV file appears to be malformed')
|
|
373
|
+
sys.exit(1)
|
|
374
|
+
except Exception as e:
|
|
375
|
+
print(f'[ERROR] Failed to read CSV: {e}')
|
|
376
|
+
sys.exit(1)
|
|
377
|
+
"; then
|
|
378
|
+
echo "[ERROR] CSV validation failed. Please check the error message above."
|
|
379
|
+
exit 1
|
|
380
|
+
fi
|
|
381
|
+
fi
|
|
382
|
+
|
|
383
|
+
# Automatic cleanup detection - check for unchanged algorithms and warn user
|
|
384
|
+
echo "[DISPATCHER] Checking for duplicate/unchanged algorithms..."
|
|
385
|
+
cleanup_output=$("$SCRIPT_DIR/claude-evolve-cleanup" --dry-run 2>&1)
|
|
386
|
+
|
|
387
|
+
# Check if cleanup found any issues (look for "UNCHANGED:" in output)
|
|
388
|
+
if echo "$cleanup_output" | grep -q "📋 UNCHANGED:"; then
|
|
389
|
+
echo "⚠️ WARNING: Issues detected that may need cleanup:"
|
|
390
|
+
echo "$cleanup_output"
|
|
391
|
+
echo ""
|
|
392
|
+
echo "🔧 RECOMMENDATION: Run 'claude-evolve cleanup --force' to fix these issues before continuing"
|
|
393
|
+
echo " This will delete unchanged algorithms and reset their descendants to pending status"
|
|
394
|
+
echo ""
|
|
395
|
+
echo "⏰ Continuing in 10 seconds (Ctrl+C to abort and run cleanup)..."
|
|
396
|
+
|
|
397
|
+
# Give user time to read and potentially abort
|
|
398
|
+
for i in {10..1}; do
|
|
399
|
+
echo -n " $i..."
|
|
400
|
+
sleep 1
|
|
401
|
+
done
|
|
402
|
+
echo ""
|
|
403
|
+
echo "🚀 Proceeding with evolution run..."
|
|
404
|
+
echo ""
|
|
405
|
+
else
|
|
406
|
+
echo "[DISPATCHER] No cleanup issues detected - proceeding with run"
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
# Consecutive failure tracking
|
|
410
|
+
consecutive_failures=0
|
|
411
|
+
MAX_CONSECUTIVE_FAILURES=5
|
|
412
|
+
|
|
282
413
|
# Main dispatch loop
|
|
283
414
|
while true; do
|
|
284
415
|
# Clean up finished workers
|
|
@@ -337,7 +468,7 @@ while true; do
|
|
|
337
468
|
done
|
|
338
469
|
|
|
339
470
|
# Brief pause to avoid busy waiting
|
|
340
|
-
sleep
|
|
471
|
+
sleep 5
|
|
341
472
|
done
|
|
342
473
|
|
|
343
474
|
# Clean shutdown
|