claude-evolve 1.3.41 → 1.3.43

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.
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Clean up malformed CSV files by removing excessive trailing empty fields.
4
+ Fixes issues where rows have too many trailing commas.
5
+ """
6
+
7
+ import csv
8
+ import sys
9
+ import os
10
+ import argparse
11
+ from typing import List
12
+
13
+ def clean_csv_row(row: List[str], expected_min_cols: int = 5) -> List[str]:
14
+ """Remove trailing empty fields from a CSV row, keeping minimum required columns."""
15
+ # Remove trailing empty strings
16
+ while len(row) > expected_min_cols and row[-1] == '':
17
+ row.pop()
18
+
19
+ # Ensure we have at least the minimum required columns
20
+ while len(row) < expected_min_cols:
21
+ row.append('')
22
+
23
+ return row
24
+
25
+ def clean_csv_file(filepath: str, backup: bool = True, dry_run: bool = False) -> int:
26
+ """Clean a CSV file by removing excessive trailing empty fields."""
27
+
28
+ if not os.path.exists(filepath):
29
+ print(f"❌ File not found: {filepath}")
30
+ return 1
31
+
32
+ # Read the original CSV
33
+ with open(filepath, 'r') as f:
34
+ reader = csv.reader(f)
35
+ headers = next(reader, [])
36
+ rows = list(reader)
37
+
38
+ if not headers:
39
+ print(f"❌ Empty CSV file: {filepath}")
40
+ return 1
41
+
42
+ print(f"📊 Original CSV: {len(rows)} rows, max {max(len(row) for row in rows) if rows else 0} columns")
43
+
44
+ # Clean each row
45
+ cleaned_rows = []
46
+ changes_made = 0
47
+
48
+ for i, row in enumerate(rows):
49
+ original_len = len(row)
50
+ cleaned_row = clean_csv_row(row.copy())
51
+ cleaned_rows.append(cleaned_row)
52
+
53
+ if len(cleaned_row) != original_len:
54
+ changes_made += 1
55
+ if dry_run:
56
+ print(f" 🔧 Row {i+2}: {original_len} → {len(cleaned_row)} columns")
57
+
58
+ if changes_made == 0:
59
+ print("✅ No changes needed - CSV is already clean")
60
+ return 0
61
+
62
+ print(f"🔧 Cleaned {changes_made} rows")
63
+
64
+ if dry_run:
65
+ print("🔍 Dry run mode - no changes written")
66
+ return 0
67
+
68
+ # Create backup if requested
69
+ if backup:
70
+ backup_file = f"{filepath}.backup.{os.getpid()}"
71
+ os.rename(filepath, backup_file)
72
+ print(f"💾 Backup created: {backup_file}")
73
+
74
+ # Write cleaned CSV
75
+ with open(filepath, 'w', newline='') as f:
76
+ writer = csv.writer(f)
77
+ writer.writerow(headers)
78
+ writer.writerows(cleaned_rows)
79
+
80
+ new_max_cols = max(len(row) for row in cleaned_rows) if cleaned_rows else 0
81
+ print(f"✅ Cleaned CSV: {len(cleaned_rows)} rows, max {new_max_cols} columns")
82
+
83
+ return 0
84
+
85
+ def main():
86
+ parser = argparse.ArgumentParser(description="Clean up malformed CSV files")
87
+ parser.add_argument("csv_file", help="Path to CSV file to clean")
88
+ parser.add_argument("--no-backup", action="store_true", help="Don't create backup file")
89
+ parser.add_argument("--dry-run", action="store_true", help="Show what would be changed without making changes")
90
+
91
+ args = parser.parse_args()
92
+
93
+ try:
94
+ return clean_csv_file(args.csv_file, backup=not args.no_backup, dry_run=args.dry_run)
95
+ except Exception as e:
96
+ print(f"❌ Error: {e}")
97
+ return 1
98
+
99
+ if __name__ == "__main__":
100
+ sys.exit(main())
@@ -25,23 +25,27 @@ USAGE:
25
25
  SELECTORS:
26
26
  gen01, gen02, etc. Target specific generation
27
27
  all Target all generations
28
- failed Target all candidates with failed status
28
+ failed Target all candidates with failed status (includes retries)
29
29
  complete Target all candidates with complete status
30
30
  pending Target all candidates with pending status
31
31
  running Target all candidates with running status
32
32
 
33
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)
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
+ failed-retry1 Mark candidates for retry attempt 1 (bug fixing)
38
+ failed-retry2 Mark candidates for retry attempt 2 (bug fixing)
39
+ failed-retry3 Mark candidates for retry attempt 3 (bug fixing)
40
+ reboot Reset completely (delete .py files, clear scores, set pending)
38
41
 
39
42
  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)
43
+ claude-evolve edit gen03 failed # Mark all gen03 as failed
44
+ claude-evolve edit failed pending # Reset all failed candidates to pending
45
+ claude-evolve edit failed failed-retry1 # Convert failed to retry status (bug fixing)
46
+ claude-evolve edit complete failed # Mark all complete as failed for re-run
47
+ claude-evolve edit all pending # Mark everything as pending for re-run
48
+ claude-evolve edit gen02 reboot # Full reset of gen02 (delete files + clear data)
45
49
 
46
50
  DESCRIPTION:
47
51
  This command helps manage evolution runs when you need to re-evaluate candidates.
@@ -73,9 +77,9 @@ fi
73
77
 
74
78
  # Validate action
75
79
  case "$ACTION" in
76
- failed|complete|pending|reboot) ;;
80
+ failed|complete|pending|failed-retry1|failed-retry2|failed-retry3|reboot) ;;
77
81
  *)
78
- echo "[ERROR] Action must be one of: failed, complete, pending, reboot" >&2
82
+ echo "[ERROR] Action must be one of: failed, complete, pending, failed-retry1, failed-retry2, failed-retry3, reboot" >&2
79
83
  exit 1
80
84
  ;;
81
85
  esac
@@ -100,12 +104,14 @@ update_candidates_status() {
100
104
  import csv
101
105
  import sys
102
106
  import os
107
+ import re
103
108
 
104
109
  csv_file = '$FULL_CSV_PATH'
105
110
  selector = '$selector'
106
111
  new_status = '$new_status'
107
112
  clear_scores = '$clear_scores' == 'true'
108
113
 
114
+
109
115
  try:
110
116
  # Read CSV
111
117
  with open(csv_file, 'r') as f:
@@ -139,6 +145,8 @@ try:
139
145
  # Status selector
140
146
  if selector == 'pending':
141
147
  matches = current_status == '' or current_status == 'pending'
148
+ elif selector == 'failed':
149
+ matches = current_status.startswith('failed')
142
150
  else:
143
151
  matches = current_status == selector
144
152
 
@@ -210,10 +218,12 @@ delete_evolution_files() {
210
218
  candidates_to_delete=$("$PYTHON_CMD" -c "
211
219
  import csv
212
220
  import sys
221
+ import re
213
222
 
214
223
  csv_file = '$FULL_CSV_PATH'
215
224
  selector = '$selector'
216
225
 
226
+
217
227
  try:
218
228
  with open(csv_file, 'r') as f:
219
229
  reader = csv.reader(f)
@@ -231,6 +241,8 @@ try:
231
241
  matches = False
232
242
  if selector == 'pending':
233
243
  matches = current_status == '' or current_status == 'pending'
244
+ elif selector == 'failed':
245
+ matches = current_status.startswith('failed')
234
246
  else:
235
247
  matches = current_status == selector
236
248
 
@@ -278,6 +290,15 @@ case "$ACTION" in
278
290
  pending)
279
291
  update_candidates_status "$SELECTOR" "" "false" # Empty status means pending
280
292
  ;;
293
+ failed-retry1)
294
+ update_candidates_status "$SELECTOR" "failed-retry1" "false"
295
+ ;;
296
+ failed-retry2)
297
+ update_candidates_status "$SELECTOR" "failed-retry2" "false"
298
+ ;;
299
+ failed-retry3)
300
+ update_candidates_status "$SELECTOR" "failed-retry3" "false"
301
+ ;;
281
302
  reboot)
282
303
  echo "[INFO] Performing full reboot of '$SELECTOR'..."
283
304
  delete_evolution_files "$SELECTOR"
@@ -328,10 +328,25 @@ CRITICAL CSV FORMAT RULES:
328
328
  * Are impossible given the codebase structure
329
329
  * Would break the algorithm interface requirements
330
330
 
331
+ ⚠️ AVOID ONLY: Kelly floor/cap adjustments that assume leverage > 1.0 (these get clamped and have no effect)
332
+
333
+ ✅ EXPLORE ANY CREATIVE IDEAS INCLUDING:
334
+ - **Machine Learning**: Neural networks, decision trees, ensemble methods (use train() method properly)
335
+ - **New Indicators**: Custom combinations, alternative calculations, multi-timeframe signals
336
+ - **Market Regime Detection**: VIX patterns, correlation shifts, volume analysis, cross-asset signals
337
+ - **Risk Management**: Dynamic stops, correlation-based position sizing, drawdown protection
338
+ - **Timing**: Time-of-day effects, calendar patterns, volatility timing
339
+ - **Alternative Strategies**: New sub-strategies, momentum scoring, mean reversion variants
340
+ - **Cross-Asset Signals**: Bond yields, sector rotation, crypto correlations
341
+ - **Multi-Timeframe**: Combining 30m/1h/daily signals for confirmation
342
+ - **Advanced Exits**: Profit targets, time-based exits, volatility-based exits
343
+
344
+ Think outside the box! The codebase supports sophisticated approaches - be creative and ambitious.
345
+
331
346
  Example descriptions:
332
- - Use ensemble of 3 random forests with different feature subsets
333
- - Replace neural network with gradient boosting decision trees
334
- - Implement Monte Carlo tree search for feature selection
347
+ - Train LSTM network on 30-day OHLCV sequences to predict next-day direction probability
348
+ - Add cross-correlation filter that reduces positions when TQQQ correlation with QQQ breaks down
349
+ - Implement intraday momentum using 30-minute data to adjust daily position sizes
335
350
 
336
351
  Add exactly $count rows to the CSV file now."
337
352
 
@@ -391,10 +406,22 @@ CRITICAL CSV FORMAT RULES:
391
406
  * What changes made this algorithm successful vs its parent
392
407
  * What parameter ranges make sense given the implementation
393
408
 
409
+ ⚠️ AVOID ONLY: Kelly floor/cap adjustments that assume leverage > 1.0 (these get clamped and have no effect)
410
+
411
+ ✅ EXPLORE PARAMETER TUNING INCLUDING:
412
+ - **Entry/Exit Thresholds**: IBS_BUY_THRESHOLD, LARISSA_LOW_THRESHOLD, RSI levels, etc.
413
+ - **Indicator Periods**: ATR_PERIOD, RSI_PERIOD, moving average lengths, etc.
414
+ - **Strategy Weights**: Emphasize best performers or rebalance for diversification
415
+ - **Risk Parameters**: Stop levels, timeout periods, correlation thresholds
416
+ - **Regime Parameters**: Volatility thresholds, trend detection sensitivity
417
+ - **ML Hyperparameters**: Learning rates, network sizes, ensemble weights (if using ML)
418
+
419
+ Be creative with parameter combinations and ranges - the system is sophisticated!
420
+
394
421
  Example descriptions:
395
- - Increase learning rate from 0.001 to 0.01 for faster convergence
396
- - Reduce batch size from 32 to 16 to improve gradient estimates
397
- - Set dropout rate to 0.3 instead of 0.1 to prevent overfitting
422
+ - Lower IBS_BUY_THRESHOLD from 0.15 to 0.12 to enter deeper oversold conditions
423
+ - Increase TRS_RSI_PERIOD from 2 to 3 for smoother RSI signals
424
+ - Raise WEIGHT_TDD from 0.38 to 0.42 to emphasize best performing strategy
398
425
 
399
426
  Add exactly $count parameter tuning rows to the CSV file now."
400
427
 
@@ -454,10 +481,23 @@ CRITICAL CSV FORMAT RULES:
454
481
  * What architectural decisions led to this algorithm's success
455
482
  * Which components are essential vs which can be replaced
456
483
 
484
+ ⚠️ AVOID ONLY: Kelly floor/cap adjustments that assume leverage > 1.0 (these get clamped and have no effect)
485
+
486
+ ✅ EXPLORE STRUCTURAL INNOVATIONS INCLUDING:
487
+ - **Algorithm Architecture**: Replace sub-strategies, change combination logic, add new layers
488
+ - **Indicator Swaps**: RSI → Stochastic, SMA → Hull MA, Bollinger → Keltner, etc.
489
+ - **Machine Learning Integration**: Add neural networks, decision trees, reinforcement learning
490
+ - **Market Regime Systems**: Multi-regime detection, regime-specific strategies
491
+ - **Risk Management Overhauls**: Portfolio heat, correlation-based sizing, adaptive stops
492
+ - **Multi-Asset Integration**: Cross-asset signals, sector rotation, bond/equity relationships
493
+ - **Time-Based Innovations**: Intraday patterns, calendar effects, volatility timing
494
+
495
+ The codebase is flexible - think architecturally about major improvements!
496
+
457
497
  Example descriptions:
458
- - Replace linear layers with convolutional layers for spatial feature learning
459
- - Use bidirectional LSTM instead of unidirectional for better context
460
- - Add residual connections between layers to improve gradient flow
498
+ - Replace 2-period RSI with LSTM-predicted momentum scores for TRS strategy
499
+ - Add ensemble voting system where sub-strategies vote on market regime
500
+ - Implement hierarchical risk budgeting with correlation-adjusted position sizing
461
501
 
462
502
  Add exactly $count structural modification rows to the CSV file now."
463
503
 
@@ -517,10 +557,23 @@ CRITICAL CSV FORMAT RULES:
517
557
  * Understand which components are compatible for merging
518
558
  * Ensure the combined approach is technically feasible in the codebase
519
559
 
560
+ ⚠️ AVOID ONLY: Kelly floor/cap adjustments that assume leverage > 1.0 (these get clamped and have no effect)
561
+
562
+ ✅ EXPLORE CREATIVE COMBINATIONS INCLUDING:
563
+ - **Strategy Fusion**: Merge successful sub-strategies, combine entry/exit logic
564
+ - **Indicator Blending**: Mix different technical analysis approaches from successful algorithms
565
+ - **Machine Learning Hybrids**: Combine ML predictions with rule-based systems
566
+ - **Multi-Regime Integration**: Blend different regime detection methods
567
+ - **Risk System Combinations**: Merge multiple risk management approaches
568
+ - **Cross-Asset Blends**: Combine internal signals with external market data
569
+ - **Multi-Timeframe Fusion**: Blend signals from different time horizons
570
+
571
+ Think creatively about what worked in different algorithms and how to combine them!
572
+
520
573
  Example descriptions:
521
- - Combine ensemble voting from algorithm 3 with feature selection from algorithm 5
522
- - Use the attention mechanism from algorithm 2 with the optimizer from algorithm 4
523
- - Merge the preprocessing pipeline from algorithm 1 with the architecture from algorithm 6
574
+ - Combine VIX regime filter from algorithm 3 with LSTM predictions from algorithm 5
575
+ - Merge volatility regime detection with machine learning momentum scoring
576
+ - Integrate multi-timeframe signals with correlation-based position adjustments
524
577
 
525
578
  Add exactly $count hybrid combination rows to the CSV file now."
526
579
 
@@ -591,6 +644,20 @@ CRITICAL CSV FORMAT RULES:
591
644
  - Leave performance and status fields completely empty (just commas)
592
645
  - Use proper CSV quoting only when descriptions contain commas
593
646
 
647
+ ⚠️ AVOID ONLY: Kelly floor/cap adjustments that assume leverage > 1.0 (these get clamped and have no effect)
648
+
649
+ ✅ EXPLORE ALL CREATIVE POSSIBILITIES INCLUDING:
650
+ - **Machine Learning**: Neural networks, ensemble methods, reinforcement learning (use train() method)
651
+ - **Advanced Indicators**: Custom combinations, multi-timeframe signals, cross-asset indicators
652
+ - **Market Regime Detection**: VIX patterns, correlation analysis, volatility clustering
653
+ - **Risk Management**: Dynamic stops, portfolio heat, correlation-based position sizing
654
+ - **Alternative Strategies**: New sub-strategies, momentum variants, mean reversion innovations
655
+ - **Multi-Asset Signals**: Sector rotation, bond yields, commodity signals
656
+ - **Time-Based Patterns**: Intraday effects, calendar anomalies, volatility timing
657
+ - **Parameter Optimization**: Entry thresholds, indicator periods, strategy weights
658
+
659
+ Think outside the box - the system is sophisticated and can handle advanced approaches!
660
+
594
661
  Add exactly $TOTAL_IDEAS algorithm variation rows to the CSV file now."
595
662
 
596
663
  echo "[INFO] Generating $TOTAL_IDEAS ideas (legacy mode)..."
@@ -236,7 +236,6 @@ 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
240
239
  else
241
240
  local exit_code=$?
242
241
  if [[ $exit_code -eq 2 ]]; then
@@ -244,24 +243,7 @@ cleanup_workers() {
244
243
  # Rate limits don't count as consecutive failures
245
244
  else
246
245
  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 PAUSED: Multiple consecutive failures detected" >&2
253
- echo "⚠️ $consecutive_failures consecutive worker failures - indicates systemic issues" >&2
254
- echo "⚠️ Possible causes: Claude API issues, evaluator bugs, configuration problems" >&2
255
- echo "⚠️ Check recent worker logs in logs/ directory for specific error details" >&2
256
- echo "⚠️ Fix issues before restarting evolution" >&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"
246
+ # With retry mechanism, failures are normal - just keep processing
265
247
  fi
266
248
  fi
267
249
  fi
@@ -406,9 +388,8 @@ else
406
388
  echo "[DISPATCHER] No cleanup issues detected - proceeding with run"
407
389
  fi
408
390
 
409
- # Consecutive failure tracking
410
- consecutive_failures=0
411
- MAX_CONSECUTIVE_FAILURES=5
391
+ # With retry mechanism, we don't need consecutive failure tracking
392
+ # Failures are handled gracefully through the retry system
412
393
 
413
394
  # Main dispatch loop
414
395
  while true; do
@@ -105,12 +105,23 @@ fi
105
105
  "$PYTHON_CMD" -c "
106
106
  import csv
107
107
  import sys
108
+ import re
108
109
 
109
110
  csv_file = '$FULL_CSV_PATH'
110
111
  show_brief = '$SHOW_BRIEF' == 'true'
111
112
  show_winner_only = '$SHOW_WINNER_ONLY' == 'true'
112
113
  evolution_context = '$EVOLUTION_CONTEXT'
113
114
 
115
+ def normalize_status(status):
116
+ '''Convert retry statuses to base status for counting.'''
117
+ if status.startswith('failed'):
118
+ return 'failed'
119
+ return status
120
+
121
+ def is_retry_status(status):
122
+ '''Check if status is a retry status.'''
123
+ return bool(re.match(r'^failed-retry[0-9]+$', status))
124
+
114
125
  try:
115
126
  with open(csv_file, 'r') as f:
116
127
  reader = csv.reader(f)
@@ -126,6 +137,7 @@ try:
126
137
  all_candidates = []
127
138
  stats_by_gen = {}
128
139
  total_stats = {'pending': 0, 'complete': 0, 'failed': 0, 'running': 0}
140
+ retry_count = 0
129
141
 
130
142
  for row in rows[1:]:
131
143
  if len(row) >= 1 and row[0]: # Must have an ID
@@ -139,19 +151,26 @@ try:
139
151
  status = row[4] if len(row) > 4 and row[4] else 'pending'
140
152
  performance = row[3] if len(row) > 3 and row[3] else ''
141
153
 
154
+ # Normalize status (failed-retry* becomes failed)
155
+ normalized_status = normalize_status(status)
156
+
157
+ # Count retries
158
+ if is_retry_status(status):
159
+ retry_count += 1
160
+
142
161
  # Track by generation
143
162
  if gen not in stats_by_gen:
144
163
  stats_by_gen[gen] = {'pending': 0, 'complete': 0, 'failed': 0, 'running': 0}
145
164
 
146
- if status in stats_by_gen[gen]:
147
- stats_by_gen[gen][status] += 1
148
- total_stats[status] += 1
165
+ if normalized_status in stats_by_gen[gen]:
166
+ stats_by_gen[gen][normalized_status] += 1
167
+ total_stats[normalized_status] += 1
149
168
  else:
150
169
  stats_by_gen[gen]['pending'] += 1
151
170
  total_stats['pending'] += 1
152
171
 
153
172
  # Collect for winner analysis (only completed with valid scores)
154
- if status == 'complete' and performance:
173
+ if normalized_status == 'complete' and performance:
155
174
  try:
156
175
  score = float(performance)
157
176
  description = row[2] if len(row) > 2 else 'No description'
@@ -183,7 +202,10 @@ try:
183
202
  print(f'📊 OVERALL: {total_candidates} total candidates')
184
203
  print(f' • {total_stats[\"pending\"]} pending')
185
204
  print(f' • {total_stats[\"complete\"]} complete')
186
- print(f'{total_stats[\"failed\"]} failed')
205
+ failed_display = f'{total_stats[\"failed\"]} failed'
206
+ if retry_count > 0:
207
+ failed_display += f' ({retry_count} retries)'
208
+ print(f' • {failed_display}')
187
209
  print(f' • {total_stats[\"running\"]} running')
188
210
  print()
189
211
 
@@ -18,6 +18,59 @@ cleanup_temp() {
18
18
  # Set trap to clean up temp files on exit
19
19
  trap cleanup_temp EXIT INT TERM
20
20
 
21
+ # Function to handle failures with retry logic
22
+ handle_failure() {
23
+ local candidate_id="$1"
24
+ local current_status="$2"
25
+ local performance="${3:-0}"
26
+
27
+ # If this is already a retry, increment the retry count
28
+ if [[ $current_status =~ ^failed-retry([0-9]+)$ ]]; then
29
+ local retry_num=${BASH_REMATCH[1]}
30
+ local new_retry_num=$((retry_num + 1))
31
+
32
+ if [[ $new_retry_num -le $MAX_RETRIES ]]; then
33
+ local new_status="failed-retry${new_retry_num}"
34
+ update_csv_row_with_lock "$candidate_id" "status" "$new_status"
35
+ update_csv_row_with_lock "$candidate_id" "performance" "$performance"
36
+ echo "[WORKER-$$] ✗ Retry $retry_num failed, marked as $new_status"
37
+ exit 1
38
+ else
39
+ update_csv_row_with_lock "$candidate_id" "status" "failed"
40
+ update_csv_row_with_lock "$candidate_id" "performance" "$performance"
41
+ echo "[WORKER-$$] ✗ Max retries ($MAX_RETRIES) exceeded, marking as permanently failed"
42
+ exit 1
43
+ fi
44
+ elif [[ $current_status == "failed" ]]; then
45
+ # Initial failure, convert to retry1 if retries are enabled
46
+ if [[ $MAX_RETRIES -gt 0 ]]; then
47
+ update_csv_row_with_lock "$candidate_id" "status" "failed-retry1"
48
+ update_csv_row_with_lock "$candidate_id" "performance" "$performance"
49
+ echo "[WORKER-$$] ✗ Initial failure, marked as failed-retry1 for retry"
50
+ exit 1
51
+ else
52
+ update_csv_row_with_lock "$candidate_id" "status" "failed"
53
+ update_csv_row_with_lock "$candidate_id" "performance" "$performance"
54
+ echo "[WORKER-$$] ✗ Failed (retries disabled)"
55
+ # Use exit code 1 - systemic issue since retries are disabled
56
+ exit 1
57
+ fi
58
+ else
59
+ # Not a failure scenario, convert to retry1 if retries enabled
60
+ if [[ $MAX_RETRIES -gt 0 ]]; then
61
+ update_csv_row_with_lock "$candidate_id" "status" "failed-retry1"
62
+ update_csv_row_with_lock "$candidate_id" "performance" "$performance"
63
+ echo "[WORKER-$$] ✗ Evaluation failed, marked as failed-retry1 for retry"
64
+ exit 1
65
+ else
66
+ update_csv_row_with_lock "$candidate_id" "status" "failed"
67
+ update_csv_row_with_lock "$candidate_id" "performance" "$performance"
68
+ echo "[WORKER-$$] ✗ Evaluation failed (retries disabled)"
69
+ exit 1
70
+ fi
71
+ fi
72
+ }
73
+
21
74
  # Load configuration
22
75
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
76
  # shellcheck source=../lib/config.sh
@@ -59,14 +112,25 @@ done
59
112
 
60
113
  # If no ID provided, find next pending
61
114
  if [[ -z $candidate_id ]]; then
62
- candidate_id=$(find_next_pending_with_lock)
63
- if [[ -z $candidate_id ]]; then
115
+ candidate_result=$(find_next_pending_with_lock)
116
+ if [[ -z $candidate_result ]]; then
64
117
  echo "[DEBUG] No pending candidates found" >&2
65
118
  exit 0
66
119
  fi
120
+
121
+ # Parse candidate_id|original_status format
122
+ if [[ $candidate_result == *"|"* ]]; then
123
+ candidate_id="${candidate_result%|*}" # Everything before |
124
+ original_candidate_status="${candidate_result#*|}" # Everything after |
125
+ else
126
+ # Fallback for old format (shouldn't happen)
127
+ candidate_id="$candidate_result"
128
+ original_candidate_status=""
129
+ fi
67
130
  else
68
131
  # Mark specified candidate as running
69
132
  update_csv_row_with_lock "$candidate_id" "status" "running"
133
+ original_candidate_status="" # Unknown for manually specified candidates
70
134
  fi
71
135
 
72
136
  echo "[WORKER-$$] Processing candidate ID: $candidate_id"
@@ -124,6 +188,23 @@ fi
124
188
  echo "[WORKER-$$] Description: $description"
125
189
  echo "[WORKER-$$] Based on ID: $based_on_id"
126
190
 
191
+ # AIDEV-NOTE: Retry logic - detect if this is a retry attempt
192
+ is_retry=false
193
+ retry_count=0
194
+ # Use original_candidate_status for retry detection (if available), otherwise fall back to CSV status
195
+ retry_status="$original_candidate_status"
196
+ if [[ -z "$retry_status" ]]; then
197
+ retry_status="$status"
198
+ fi
199
+
200
+ if [[ $retry_status =~ ^failed-retry([0-9]+)$ ]]; then
201
+ is_retry=true
202
+ retry_count=${BASH_REMATCH[1]}
203
+ echo "[WORKER-$$] 🔄 Processing retry attempt #$retry_count"
204
+ elif [[ $retry_status == "failed" ]]; then
205
+ echo "[WORKER-$$] ⚠️ Initial failure detected - this should be converted to failed-retry1 to enable retries"
206
+ fi
207
+
127
208
  # AIDEV-NOTE: Using common evolution processor logic for consistent handling
128
209
  # Determine parent algorithm
129
210
  if [[ -z $based_on_id || $based_on_id == "0" || $based_on_id == '""' ]]; then
@@ -139,7 +220,7 @@ else
139
220
 
140
221
  if [[ ! -f $parent_file ]]; then
141
222
  echo "[ERROR] Parent algorithm not found: $parent_file" >&2
142
- update_csv_row_with_lock "$candidate_id" "status" "failed"
223
+ handle_failure "$candidate_id" "$retry_status" "0"
143
224
  exit 1
144
225
  fi
145
226
  fi
@@ -155,11 +236,28 @@ fi
155
236
  temp_file="${output_file}.tmp$$"
156
237
 
157
238
  # Check if processing should be skipped using common logic
239
+ # Set environment variable for retry detection
240
+ if [[ $is_retry == "true" ]]; then
241
+ export RETRY_CANDIDATE=true
242
+ else
243
+ export RETRY_CANDIDATE=false
244
+ fi
245
+
158
246
  eval "$("$PYTHON_CMD" "$SCRIPT_DIR/../lib/evolution_processor.py" "$id" "$based_on_id" "$FULL_OUTPUT_DIR" "$ROOT_DIR" "$parent_file" "$output_file")"
159
247
 
160
248
  # Handle copy operation to temp file
161
249
  if [[ "$skip_copy" == "True" ]]; then
162
250
  echo "[WORKER-$$] ⚠️ Skipping copy - $reason"
251
+ elif [[ $is_retry == "true" ]]; then
252
+ # For retries, edit the existing failed algorithm in-place
253
+ if [[ -f "$output_file" ]]; then
254
+ cp "$output_file" "$temp_file"
255
+ echo "[WORKER-$$] 🔄 Copied existing algorithm for retry: $temp_file"
256
+ else
257
+ # Fallback to parent if existing file doesn't exist
258
+ cp "$parent_file" "$temp_file"
259
+ echo "[WORKER-$$] ⚠️ Existing algorithm not found, using parent: $temp_file"
260
+ fi
163
261
  else
164
262
  cp "$parent_file" "$temp_file"
165
263
  echo "[WORKER-$$] Copied parent to temp file: $temp_file"
@@ -178,15 +276,28 @@ else
178
276
  claude_cmd="${CLAUDE_CMD:-claude}"
179
277
  if ! command -v "$claude_cmd" >/dev/null 2>&1; then
180
278
  echo "[ERROR] Claude CLI not found" >&2
181
- update_csv_row_with_lock "$candidate_id" "status" "failed"
279
+ handle_failure "$candidate_id" "$retry_status" "0"
182
280
  exit 1
183
281
  fi
184
282
 
185
283
  CLAUDE_MODEL="sonnet"
186
284
  echo "[WORKER-$$] Using Claude $CLAUDE_MODEL for mutation"
187
285
 
188
- # Create mutation prompt
189
- prompt="Edit the file $temp_file to implement this specific change: $description
286
+ # Create mutation prompt (different for retries vs initial attempts)
287
+ if [[ $is_retry == "true" ]]; then
288
+ prompt="Fix the bugs in the file $temp_file. This algorithm was attempting to implement: $description
289
+
290
+ The algorithm failed during evaluation. Please:
291
+ - Analyze the code for potential bugs (syntax errors, logical issues, missing imports, etc.)
292
+ - Fix any problems you find
293
+ - Ensure the code runs without errors
294
+ - Make sure it still implements the intended change: $description
295
+ - Add appropriate error handling and validation
296
+ - If possible, suggest a simple way to test this fix
297
+
298
+ This is retry attempt #$retry_count. Focus on making the code robust and correct."
299
+ else
300
+ prompt="Edit the file $temp_file to implement this specific change: $description
190
301
 
191
302
  Requirements:
192
303
  - Edit the file directly (don't just provide comments or suggestions)
@@ -196,6 +307,7 @@ Requirements:
196
307
  - Add proper error handling if needed
197
308
 
198
309
  The file currently contains the parent algorithm. Modify it according to the description above."
310
+ fi
199
311
 
200
312
  # Log prompt
201
313
  {
@@ -235,7 +347,7 @@ The file currently contains the parent algorithm. Modify it according to the des
235
347
  rm "$temp_file"
236
348
  echo "[WORKER-$$] Cleaned up temp file due to Claude failure" >&2
237
349
  fi
238
- update_csv_row_with_lock "$candidate_id" "status" "failed"
350
+ handle_failure "$candidate_id" "$retry_status" "0"
239
351
  exit 1
240
352
  fi
241
353
 
@@ -249,7 +361,7 @@ The file currently contains the parent algorithm. Modify it according to the des
249
361
 
250
362
  # Clean up temp file and mark as failed
251
363
  rm "$temp_file"
252
- update_csv_row_with_lock "$candidate_id" "status" "failed"
364
+ handle_failure "$candidate_id" "$retry_status" "0"
253
365
  exit 1
254
366
  else
255
367
  # Changes were made - move temp file to final location
@@ -306,9 +418,7 @@ if [[ $eval_exit_code -eq 0 ]]; then
306
418
  if [[ $eval_output =~ ^[[:space:]]*-?[0-9]+\.?[0-9]*[[:space:]]*$ ]]; then
307
419
  score=$(echo "$eval_output" | tr -d ' ')
308
420
  if [[ $(echo "$score == 0" | bc -l) == "1" ]]; then
309
- update_csv_row_with_lock "$candidate_id" "status" "failed"
310
- update_csv_row_with_lock "$candidate_id" "performance" "$score"
311
- echo "[WORKER-$$] ✗ Evaluation failed with score 0"
421
+ handle_failure "$candidate_id" "$retry_status" "$score"
312
422
  exit 1
313
423
  else
314
424
  update_csv_row_with_lock "$candidate_id" "performance" "$score"
@@ -348,9 +458,7 @@ if [[ $eval_exit_code -eq 0 ]]; then
348
458
  if score=$(echo "$eval_output" | grep -o '"score"[[:space:]]*:[[:space:]]*[0-9.]*' | cut -d: -f2 | tr -d ' '); then
349
459
  if [[ -n $score ]]; then
350
460
  if [[ $(echo "$score == 0" | bc -l) == "1" ]]; then
351
- update_csv_row_with_lock "$candidate_id" "status" "failed"
352
- update_csv_row_with_lock "$candidate_id" "performance" "$score"
353
- echo "[WORKER-$$] ✗ Evaluation failed with score 0"
461
+ handle_failure "$candidate_id" "$retry_status" "$score"
354
462
  exit 1
355
463
  else
356
464
  update_csv_row_with_lock "$candidate_id" "performance" "$score"
@@ -365,9 +473,7 @@ if [[ $eval_exit_code -eq 0 ]]; then
365
473
  if score=$(echo "$eval_output" | grep -o '"performance"[[:space:]]*:[[:space:]]*[0-9.]*' | cut -d: -f2 | tr -d ' '); then
366
474
  if [[ -n $score ]]; then
367
475
  if [[ $(echo "$score == 0" | bc -l) == "1" ]]; then
368
- update_csv_row_with_lock "$candidate_id" "status" "failed"
369
- update_csv_row_with_lock "$candidate_id" "performance" "$score"
370
- echo "[WORKER-$$] ✗ Evaluation failed with score 0"
476
+ handle_failure "$candidate_id" "$retry_status" "$score"
371
477
  exit 1
372
478
  else
373
479
  update_csv_row_with_lock "$candidate_id" "performance" "$score"
@@ -382,10 +488,10 @@ if [[ $eval_exit_code -eq 0 ]]; then
382
488
  echo "[ERROR] Expected: plain number (e.g., 1.23) or JSON with 'score' or 'performance' field" >&2
383
489
  echo "[ERROR] Actual evaluator output was:" >&2
384
490
  echo "$eval_output" >&2
385
- update_csv_row_with_lock "$candidate_id" "status" "failed"
491
+ handle_failure "$candidate_id" "$retry_status" "0"
386
492
  exit 1
387
493
  else
388
494
  echo "[ERROR] Evaluator failed with exit code $eval_exit_code" >&2
389
- update_csv_row_with_lock "$candidate_id" "status" "failed"
495
+ handle_failure "$candidate_id" "$retry_status" "0"
390
496
  exit 1
391
497
  fi
package/lib/config.sh CHANGED
@@ -45,6 +45,9 @@ DEFAULT_LOCK_TIMEOUT=10
45
45
  # Default auto ideation value
46
46
  DEFAULT_AUTO_IDEATE=true
47
47
 
48
+ # Default retry value
49
+ DEFAULT_MAX_RETRIES=3
50
+
48
51
  # Load configuration from config file
49
52
  load_config() {
50
53
  # Accept config file path as parameter
@@ -76,6 +79,9 @@ load_config() {
76
79
  # Set auto ideation default
77
80
  AUTO_IDEATE="$DEFAULT_AUTO_IDEATE"
78
81
 
82
+ # Set retry default
83
+ MAX_RETRIES="$DEFAULT_MAX_RETRIES"
84
+
79
85
  # Load config if found
80
86
  if [[ -f "$config_file" ]]; then
81
87
  echo "[DEBUG] Loading configuration from: $config_file" >&2
@@ -151,6 +157,7 @@ load_config() {
151
157
  parent_selection) PARENT_SELECTION="$value" ;;
152
158
  python_cmd) PYTHON_CMD="$value" ;;
153
159
  auto_ideate) AUTO_IDEATE="$value" ;;
160
+ max_retries) MAX_RETRIES="$value" ;;
154
161
  evolution_dir)
155
162
  echo "[WARN] evolution_dir in config is ignored - automatically inferred from config file location" >&2
156
163
  ;;
@@ -245,4 +252,5 @@ show_config() {
245
252
  echo " Max workers: $MAX_WORKERS"
246
253
  echo " Lock timeout: $LOCK_TIMEOUT"
247
254
  echo " Auto ideate: $AUTO_IDEATE"
255
+ echo " Max retries: $MAX_RETRIES"
248
256
  }
package/lib/csv-lock.sh CHANGED
@@ -230,29 +230,42 @@ find_next_pending_with_lock() {
230
230
  return 1
231
231
  fi
232
232
 
233
- # Find oldest pending candidate and update to running using Python
233
+ # Find oldest pending candidate (including retries) and update to running using Python
234
234
  local candidate=$("$PYTHON_CMD" -c "
235
235
  import csv
236
236
  import sys
237
+ import re
238
+
239
+ def is_pending_retry(status):
240
+ '''Check if status is pending (empty, pending, or retry status).'''
241
+ if not status or status == 'pending':
242
+ return True
243
+ return status.startswith('failed-retry')
237
244
 
238
245
  # Read CSV
239
246
  with open('$csv_file', 'r') as f:
240
247
  reader = csv.reader(f)
241
248
  rows = list(reader)
242
249
 
243
- # Find first pending candidate
250
+ # Find first pending candidate (including retries)
244
251
  candidate_id = None
252
+ original_status = None
245
253
  for i in range(1, len(rows)):
254
+ # Skip empty rows
255
+ if not rows[i] or len(rows[i]) == 0:
256
+ continue
246
257
  # If row has fewer than 5 fields, it's pending
247
258
  if len(rows[i]) < 5:
248
259
  candidate_id = rows[i][0]
260
+ original_status = '' # Empty status means pending
249
261
  # Ensure row has 5 fields before setting status
250
262
  while len(rows[i]) < 5:
251
263
  rows[i].append('')
252
264
  rows[i][4] = 'running' # Update status
253
265
  break
254
- elif len(rows[i]) >= 5 and (rows[i][4] == 'pending' or rows[i][4] == ''):
266
+ elif len(rows[i]) >= 5 and is_pending_retry(rows[i][4]):
255
267
  candidate_id = rows[i][0]
268
+ original_status = rows[i][4] # Save original status before overwriting
256
269
  rows[i][4] = 'running' # Update status
257
270
  break
258
271
 
@@ -261,7 +274,7 @@ if candidate_id:
261
274
  with open('${csv_file}.tmp', 'w', newline='') as f:
262
275
  writer = csv.writer(f)
263
276
  writer.writerows(rows)
264
- print(candidate_id)
277
+ print(f'{candidate_id}|{original_status}') # Return both ID and original status
265
278
  ")
266
279
 
267
280
  if [ -n "$candidate" ]; then
package/lib/csv_helper.py CHANGED
@@ -8,6 +8,7 @@ import csv
8
8
  import json
9
9
  import sys
10
10
  import os
11
+ import re
11
12
  from typing import Dict, List, Any
12
13
 
13
14
 
@@ -50,6 +51,40 @@ def ensure_columns(headers: list[str], rows: list[list[str]], new_fields: dict)
50
51
  return headers, rows
51
52
 
52
53
 
54
+ def parse_retry_status(status: str) -> tuple[str, int]:
55
+ """Parse retry status and return (base_status, retry_count).
56
+
57
+ Examples:
58
+ 'failed' -> ('failed', 0)
59
+ 'failed-retry1' -> ('failed', 1)
60
+ 'failed-retry3' -> ('failed', 3)
61
+ 'complete' -> ('complete', 0)
62
+ """
63
+ if not status:
64
+ return ('', 0)
65
+
66
+ match = re.match(r'^(.*)-retry(\d+)$', status)
67
+ if match:
68
+ base_status = match.group(1)
69
+ retry_count = int(match.group(2))
70
+ return (base_status, retry_count)
71
+ else:
72
+ return (status, 0)
73
+
74
+
75
+ def is_retry_candidate(status: str) -> bool:
76
+ """Check if a status represents a retry candidate."""
77
+ base_status, _ = parse_retry_status(status)
78
+ return base_status == 'failed' and status.startswith('failed-retry')
79
+
80
+
81
+ def is_pending_retry(status: str) -> bool:
82
+ """Check if status is pending (empty, 'pending', or retry status)."""
83
+ if not status or status == 'pending':
84
+ return True
85
+ return is_retry_candidate(status)
86
+
87
+
53
88
  def update_row_with_fields(headers: list[str], rows: list[list[str]], target_id: str, fields: dict):
54
89
  """Update a specific row with multiple fields."""
55
90
  # Find column indices
@@ -162,9 +197,12 @@ def main():
162
197
  try:
163
198
  headers, rows = read_csv(csv_file)
164
199
 
165
- # Find first row with empty status or status == "pending"
200
+ # Find first row with empty status, "pending", or retry status
166
201
  for i, row in enumerate(rows, start=2): # Start at 2 (1-indexed, skip header)
167
- if len(row) < 5 or row[4] == '' or row[4] == 'pending':
202
+ if len(row) < 5:
203
+ print(i)
204
+ sys.exit(0)
205
+ elif len(row) >= 5 and is_pending_retry(row[4]):
168
206
  print(i)
169
207
  sys.exit(0)
170
208
 
@@ -14,7 +14,8 @@ def should_skip_processing(id_val, based_on_id, parent_file, output_file):
14
14
  """
15
15
  Determine if evolution processing should be skipped.
16
16
 
17
- Simple rule: If file exists, skip everything. This handles all edge cases cleanly.
17
+ Simple rule: If file exists, skip everything UNLESS this is a retry candidate.
18
+ For retry candidates, we want Claude to process the existing file to fix bugs.
18
19
 
19
20
  Returns tuple: (skip_copy, skip_claude, reason)
20
21
  """
@@ -23,9 +24,15 @@ def should_skip_processing(id_val, based_on_id, parent_file, output_file):
23
24
  return True, True, "Baseline algorithm - no processing needed"
24
25
 
25
26
  # File existence check - if file exists, skip both copy and Claude
26
- # This automatically handles self-parent cases and re-runs
27
+ # EXCEPT for retry candidates which need Claude to fix the existing file
27
28
  if os.path.exists(output_file):
28
- return True, True, "File already exists - skipping all processing"
29
+ # Check if this might be a retry candidate by looking for retry status in environment
30
+ # The worker sets RETRY_CANDIDATE=true for retry processing
31
+ retry_env = os.environ.get('RETRY_CANDIDATE')
32
+ if retry_env == 'true':
33
+ return True, False, "Retry candidate - skip copy but run Claude for bug fixing"
34
+ else:
35
+ return True, True, "File already exists - skipping all processing"
29
36
 
30
37
  # File doesn't exist - proceed with copy and Claude
31
38
  return False, False, None
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-evolve",
3
- "version": "1.3.41",
3
+ "version": "1.3.43",
4
4
  "bin": {
5
5
  "claude-evolve": "./bin/claude-evolve",
6
6
  "claude-evolve-main": "./bin/claude-evolve-main",
@@ -42,6 +42,10 @@ python_cmd: "python3"
42
42
  # When true, automatically generate new ideas when no pending candidates remain
43
43
  auto_ideate: true
44
44
 
45
+ # Retry configuration
46
+ # Maximum number of retries for failed candidates before marking as permanently failed
47
+ max_retries: 3
48
+
45
49
  # Parallel execution configuration
46
50
  parallel:
47
51
  # Enable parallel execution of evolution candidates