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.
- package/bin/claude-evolve-csv-fix +100 -0
- package/bin/claude-evolve-edit +33 -12
- package/bin/claude-evolve-ideate +79 -12
- package/bin/claude-evolve-run +3 -22
- package/bin/claude-evolve-status +27 -5
- package/bin/claude-evolve-worker +125 -19
- package/lib/config.sh +8 -0
- package/lib/csv-lock.sh +17 -4
- package/lib/csv_helper.py +40 -2
- package/lib/evolution_processor.py +10 -3
- package/package.json +1 -1
- package/templates/config.yaml +4 -0
|
@@ -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())
|
package/bin/claude-evolve-edit
CHANGED
|
@@ -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
|
|
35
|
-
complete
|
|
36
|
-
pending
|
|
37
|
-
|
|
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
|
|
41
|
-
claude-evolve edit failed pending
|
|
42
|
-
claude-evolve edit
|
|
43
|
-
claude-evolve edit
|
|
44
|
-
claude-evolve edit
|
|
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"
|
package/bin/claude-evolve-ideate
CHANGED
|
@@ -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
|
-
-
|
|
333
|
-
-
|
|
334
|
-
- Implement
|
|
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
|
-
-
|
|
396
|
-
-
|
|
397
|
-
-
|
|
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
|
|
459
|
-
-
|
|
460
|
-
-
|
|
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
|
|
522
|
-
-
|
|
523
|
-
-
|
|
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)..."
|
package/bin/claude-evolve-run
CHANGED
|
@@ -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
|
-
|
|
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
|
-
#
|
|
410
|
-
|
|
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
|
package/bin/claude-evolve-status
CHANGED
|
@@ -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
|
|
147
|
-
stats_by_gen[gen][
|
|
148
|
-
total_stats[
|
|
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
|
|
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
|
-
|
|
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
|
|
package/bin/claude-evolve-worker
CHANGED
|
@@ -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
|
-
|
|
63
|
-
if [[ -z $
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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]
|
|
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
|
|
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
|
|
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
|
|
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
|
-
#
|
|
27
|
+
# EXCEPT for retry candidates which need Claude to fix the existing file
|
|
27
28
|
if os.path.exists(output_file):
|
|
28
|
-
|
|
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
package/templates/config.yaml
CHANGED
|
@@ -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
|