claude-evolve 1.3.14 → 1.3.16
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 +2 -1
- package/bin/claude-evolve-analyze +119 -59
- package/bin/claude-evolve-ideate +50 -25
- package/bin/claude-evolve-run-parallel +4 -1
- package/lib/csv-lock.sh +9 -1
- package/lib/csv_helper.py +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Think of it like **genetic algorithms for code** - it handles the mutations and
|
|
|
12
12
|
|
|
13
13
|
The system operates with specialized phases working together:
|
|
14
14
|
|
|
15
|
-
- 🧠 **Ideation Phase**: Generates creative algorithm variations using Claude Opus
|
|
15
|
+
- 🧠 **Ideation Phase**: Generates creative algorithm variations using codex o3-pro (if available) or Claude Opus
|
|
16
16
|
- 🔬 **Development Phase**: Implements mutations using Claude Sonnet (with periodic Opus "megathinking")
|
|
17
17
|
- 📊 **Evaluation Phase**: Tests performance against your custom evaluator
|
|
18
18
|
- 📈 **Analysis Phase**: Tracks evolution progress and identifies top performers
|
|
@@ -180,6 +180,7 @@ Evolution experiments can fail for various reasons. The system tracks these fail
|
|
|
180
180
|
- [Claude CLI](https://docs.anthropic.com/en/docs/claude-code) (`claude` command)
|
|
181
181
|
|
|
182
182
|
### Optional (but recommended)
|
|
183
|
+
- [Codex CLI](https://github.com/aboutgaurav/codex) (`codex` command) - Uses o3-pro model for superior ideation when available
|
|
183
184
|
- Scientific Python libraries (numpy, scipy, etc.) depending on your algorithms
|
|
184
185
|
- Plotting libraries (matplotlib, plotly) for analyzing results
|
|
185
186
|
|
|
@@ -292,16 +292,18 @@ if command -v gnuplot >/dev/null 2>&1 && [[ $valid_performance_count -gt 0 ]]; t
|
|
|
292
292
|
echo "# Row ID Performance Generation" >"$data_file"
|
|
293
293
|
echo "# Generation AvgPerformance Color" >"$gen_avg_file"
|
|
294
294
|
|
|
295
|
-
# Get color by generation number (rotates through
|
|
295
|
+
# Get color by generation number (rotates through 7 colors)
|
|
296
296
|
get_gen_color() {
|
|
297
297
|
local gen_num="$1"
|
|
298
|
-
local color_index=$((
|
|
298
|
+
local color_index=$(( gen_num % 7 ))
|
|
299
299
|
case $color_index in
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
300
|
+
0) echo "#1f77b4" ;; # blue
|
|
301
|
+
1) echo "#ff7f0e" ;; # orange
|
|
302
|
+
2) echo "#2ca02c" ;; # green
|
|
303
|
+
3) echo "#d62728" ;; # red
|
|
304
|
+
4) echo "#9467bd" ;; # purple
|
|
305
|
+
5) echo "#8c564b" ;; # brown
|
|
306
|
+
6) echo "#e377c2" ;; # pink
|
|
305
307
|
esac
|
|
306
308
|
}
|
|
307
309
|
|
|
@@ -314,76 +316,101 @@ if command -v gnuplot >/dev/null 2>&1 && [[ $valid_performance_count -gt 0 ]]; t
|
|
|
314
316
|
max_row=0
|
|
315
317
|
max_id=""
|
|
316
318
|
|
|
317
|
-
# Use Python to generate chart data
|
|
318
|
-
|
|
319
|
+
# Use Python to generate chart data
|
|
320
|
+
"$PYTHON_CMD" -c "
|
|
319
321
|
import csv
|
|
320
322
|
import re
|
|
321
323
|
|
|
322
|
-
row_num = 0
|
|
323
|
-
max_perf = 0
|
|
324
|
-
max_row = 0
|
|
325
|
-
max_id = ''
|
|
326
|
-
|
|
327
324
|
with open('$csv_file', 'r') as f:
|
|
328
325
|
reader = csv.reader(f)
|
|
329
326
|
next(reader) # Skip header
|
|
330
327
|
|
|
331
|
-
|
|
332
|
-
|
|
328
|
+
completed_order = 0 # Track order of completion
|
|
329
|
+
|
|
330
|
+
with open('$data_file', 'w') as data_f:
|
|
331
|
+
data_f.write('# Order ID Performance Generation\\n')
|
|
332
|
+
|
|
333
|
+
with open('$gen_data_temp', 'w') as gen_f:
|
|
334
|
+
pass # Clear file
|
|
335
|
+
|
|
336
|
+
max_perf = 0
|
|
337
|
+
max_id = ''
|
|
338
|
+
max_order = 0
|
|
333
339
|
|
|
334
340
|
for row in reader:
|
|
335
341
|
if len(row) < 5:
|
|
336
342
|
continue
|
|
337
343
|
|
|
338
|
-
row_num += 1
|
|
339
344
|
id, _, desc, perf, status = row[:5]
|
|
340
345
|
|
|
341
346
|
# Extract generation from ID
|
|
342
347
|
gen = 'gen01' # default
|
|
343
|
-
|
|
348
|
+
gen_num = 1
|
|
349
|
+
match = re.match(r'^gen([0-9]+)-', id)
|
|
344
350
|
if match:
|
|
345
|
-
gen = match.group(1)
|
|
351
|
+
gen = f'gen{match.group(1)}'
|
|
352
|
+
gen_num = int(match.group(1))
|
|
346
353
|
|
|
347
354
|
# Only include completed algorithms with non-zero performance
|
|
348
355
|
if perf and perf != '' and status in ['complete', 'completed']:
|
|
349
356
|
try:
|
|
350
357
|
perf_val = float(perf)
|
|
351
358
|
if perf_val > 0:
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
359
|
+
completed_order += 1
|
|
360
|
+
|
|
361
|
+
# Write to data file
|
|
362
|
+
with open('$data_file', 'a') as f:
|
|
363
|
+
f.write(f'{completed_order} \"{id}\" {perf} {gen_num}\\n')
|
|
357
364
|
|
|
358
|
-
|
|
359
|
-
|
|
365
|
+
# Write to gen temp file
|
|
366
|
+
with open('$gen_data_temp', 'a') as f:
|
|
367
|
+
f.write(f'{gen} {perf}\\n')
|
|
360
368
|
|
|
361
369
|
# Track the winner
|
|
362
370
|
if perf_val > max_perf:
|
|
363
371
|
max_perf = perf_val
|
|
364
|
-
|
|
372
|
+
max_order = completed_order
|
|
365
373
|
max_id = id
|
|
366
374
|
except ValueError:
|
|
367
375
|
pass
|
|
368
376
|
|
|
369
|
-
# Write data file
|
|
370
|
-
with open('$data_file', 'a') as f:
|
|
371
|
-
for line in data_lines:
|
|
372
|
-
f.write(line + '\\\\n')
|
|
373
|
-
|
|
374
|
-
# Write gen temp file
|
|
375
|
-
with open('$gen_data_temp', 'a') as f:
|
|
376
|
-
for line in gen_temp_lines:
|
|
377
|
-
f.write(line + '\\\\n')
|
|
378
|
-
|
|
379
377
|
# Output max values for shell
|
|
380
378
|
print(f'max_perf={max_perf}')
|
|
381
|
-
print(f'max_row={
|
|
382
|
-
print(f'max_id
|
|
383
|
-
"
|
|
379
|
+
print(f'max_row={max_order}')
|
|
380
|
+
print(f'max_id=\"{max_id}\"')
|
|
381
|
+
"
|
|
384
382
|
|
|
385
|
-
#
|
|
386
|
-
eval "$
|
|
383
|
+
# Capture the output properly
|
|
384
|
+
eval "$("$PYTHON_CMD" -c "
|
|
385
|
+
import csv
|
|
386
|
+
import re
|
|
387
|
+
|
|
388
|
+
with open('$csv_file', 'r') as f:
|
|
389
|
+
reader = csv.reader(f)
|
|
390
|
+
next(reader)
|
|
391
|
+
|
|
392
|
+
max_perf = 0
|
|
393
|
+
max_id = ''
|
|
394
|
+
max_order = 0
|
|
395
|
+
completed_order = 0
|
|
396
|
+
|
|
397
|
+
for row in reader:
|
|
398
|
+
if len(row) >= 5 and row[3] and row[4] in ['complete', 'completed']:
|
|
399
|
+
try:
|
|
400
|
+
perf_val = float(row[3])
|
|
401
|
+
if perf_val > 0:
|
|
402
|
+
completed_order += 1
|
|
403
|
+
if perf_val > max_perf:
|
|
404
|
+
max_perf = perf_val
|
|
405
|
+
max_order = completed_order
|
|
406
|
+
max_id = row[0]
|
|
407
|
+
except ValueError:
|
|
408
|
+
pass
|
|
409
|
+
|
|
410
|
+
print(f'max_perf={max_perf}')
|
|
411
|
+
print(f'max_row={max_order}')
|
|
412
|
+
print(f'max_id=\"{max_id}\"')
|
|
413
|
+
")"
|
|
387
414
|
|
|
388
415
|
# Create generation averages file and track max generation
|
|
389
416
|
gen_index=1
|
|
@@ -397,11 +424,11 @@ print(f'max_id=\\\"{max_id}\\\"')
|
|
|
397
424
|
avg=$(echo "scale=4; $sum / $count" | bc -l 2>/dev/null || echo "0")
|
|
398
425
|
gen_num=$(echo "$gen" | sed 's/gen0*//')
|
|
399
426
|
# Track max generation number
|
|
400
|
-
if [[ $gen_num -gt $max_gen_num ]]; then
|
|
427
|
+
if [[ $gen_num =~ ^[0-9]+$ ]] && [[ $gen_num -gt $max_gen_num ]]; then
|
|
401
428
|
max_gen_num=$gen_num
|
|
402
429
|
fi
|
|
403
430
|
color=$(get_gen_color "$gen_num")
|
|
404
|
-
echo "$gen_index \"$
|
|
431
|
+
echo "$gen_index \"Gen$gen_num\" $avg \"$color\"" >>"$gen_avg_file"
|
|
405
432
|
((gen_index++))
|
|
406
433
|
fi
|
|
407
434
|
fi
|
|
@@ -417,26 +444,58 @@ print(f'max_id=\\\"{max_id}\\\"')
|
|
|
417
444
|
|
|
418
445
|
# Generate dual plot
|
|
419
446
|
if [[ -s "$data_file" ]]; then
|
|
420
|
-
#
|
|
447
|
+
# Debug: show data file content
|
|
448
|
+
# echo "DEBUG: Data file content:"
|
|
449
|
+
# cat "$data_file"
|
|
450
|
+
# echo "DEBUG: max_gen_num=$max_gen_num"
|
|
451
|
+
|
|
452
|
+
# Plot all algorithms in order of completion, colored by generation
|
|
421
453
|
plot_cmd=""
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
454
|
+
gen_plots_added=0
|
|
455
|
+
|
|
456
|
+
# Find all generations that have data
|
|
457
|
+
generations=($(awk '{if(NR>1) print $4}' "$data_file" | sort -n | uniq))
|
|
458
|
+
|
|
459
|
+
for gen_num in "${generations[@]}"; do
|
|
460
|
+
if [[ -n $gen_num ]]; then
|
|
461
|
+
color=$(get_gen_color "$gen_num")
|
|
462
|
+
if [[ $gen_plots_added -gt 0 ]]; then
|
|
463
|
+
plot_cmd="$plot_cmd, \\"$'\n'
|
|
464
|
+
fi
|
|
465
|
+
plot_cmd="${plot_cmd} \"$data_file\" using (\$4==$gen_num?\$1:1/0):3 with linespoints linewidth 2 linecolor rgb \"$color\" pointsize 1.2 title \"Gen $gen_num\""
|
|
466
|
+
((gen_plots_added++))
|
|
426
467
|
fi
|
|
427
|
-
plot_cmd="${plot_cmd} \"$data_file\" using (\$4==$i?\$1:1/0):3 with linespoints linewidth 2 linecolor rgb \"$color\" pointsize 0.8 title \"Gen $i\""
|
|
428
468
|
done
|
|
469
|
+
|
|
429
470
|
# Add winner point
|
|
430
|
-
|
|
431
|
-
|
|
471
|
+
if [[ -n $max_id && -s "$winner_file" ]]; then
|
|
472
|
+
if [[ $gen_plots_added -gt 0 ]]; then
|
|
473
|
+
plot_cmd="$plot_cmd, \\"$'\n'
|
|
474
|
+
fi
|
|
475
|
+
plot_cmd="${plot_cmd} \"$winner_file\" using 1:3 with points pointtype 7 pointsize 3 linecolor rgb \"gold\" title \"Best ($max_id)\""
|
|
476
|
+
fi
|
|
432
477
|
|
|
433
|
-
#
|
|
478
|
+
# Fallback if no generation-specific plots
|
|
479
|
+
if [[ $gen_plots_added -eq 0 ]]; then
|
|
480
|
+
plot_cmd="\"$data_file\" using 1:3 with linespoints linewidth 2 linecolor rgb \"#1f77b4\" pointsize 1.2 title \"Evolution Progress\""
|
|
481
|
+
if [[ -n $max_id && -s "$winner_file" ]]; then
|
|
482
|
+
plot_cmd="$plot_cmd, \\"$'\n'
|
|
483
|
+
plot_cmd="${plot_cmd} \"$winner_file\" using 1:3 with points pointtype 7 pointsize 3 linecolor rgb \"gold\" title \"Best ($max_id)\""
|
|
484
|
+
fi
|
|
485
|
+
fi
|
|
486
|
+
|
|
487
|
+
# Build x-axis labels for generation chart (include all generations from data)
|
|
434
488
|
xtics_labels=""
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
489
|
+
label_index=1
|
|
490
|
+
for gen in $(cut -d' ' -f1 "$gen_data_temp" | sort -u); do
|
|
491
|
+
if [[ -n $gen ]]; then
|
|
492
|
+
gen_display=$(echo "$gen" | sed 's/gen0*//')
|
|
493
|
+
if [[ -n $xtics_labels ]]; then
|
|
494
|
+
xtics_labels="$xtics_labels, "
|
|
495
|
+
fi
|
|
496
|
+
xtics_labels="${xtics_labels}\"Gen$gen_display\" $label_index"
|
|
497
|
+
((label_index++))
|
|
438
498
|
fi
|
|
439
|
-
xtics_labels="${xtics_labels}\"Gen$i\" $i"
|
|
440
499
|
done
|
|
441
500
|
|
|
442
501
|
gnuplot <<EOF
|
|
@@ -448,11 +507,12 @@ set multiplot layout 2,1 margins 0.08,0.82,0.15,0.95 spacing 0.1,0.15
|
|
|
448
507
|
|
|
449
508
|
#=================== TOP PLOT: Performance Over Time ===================
|
|
450
509
|
set title "Algorithm Evolution Performance Over Time" font ",14"
|
|
451
|
-
set xlabel "
|
|
510
|
+
set xlabel "Completion Order (Algorithm #)"
|
|
452
511
|
set ylabel "Performance Score"
|
|
453
512
|
set grid
|
|
454
513
|
set key outside right
|
|
455
|
-
set xtics
|
|
514
|
+
set xtics 1
|
|
515
|
+
set autoscale y
|
|
456
516
|
|
|
457
517
|
# Define colors for generations
|
|
458
518
|
plot $plot_cmd
|
package/bin/claude-evolve-ideate
CHANGED
|
@@ -14,14 +14,34 @@ else
|
|
|
14
14
|
load_config
|
|
15
15
|
fi
|
|
16
16
|
|
|
17
|
-
# Helper function to call
|
|
18
|
-
|
|
17
|
+
# Helper function to call AI model (codex o3-pro if available, else Claude)
|
|
18
|
+
call_ai_with_limit_check() {
|
|
19
19
|
local prompt="$1"
|
|
20
|
-
local
|
|
20
|
+
local fallback_model="${2:-opus}"
|
|
21
|
+
|
|
22
|
+
# Check if codex is available
|
|
23
|
+
if command -v codex >/dev/null 2>&1; then
|
|
24
|
+
echo "[INFO] Using codex o3-pro for ideation (smartest available model)" >&2
|
|
25
|
+
|
|
26
|
+
# Call codex with o3-pro model
|
|
27
|
+
local ai_output
|
|
28
|
+
ai_output=$(echo "$prompt" | codex -m o3-pro 2>&1)
|
|
29
|
+
local ai_exit_code=$?
|
|
30
|
+
|
|
31
|
+
if [[ $ai_exit_code -eq 0 ]]; then
|
|
32
|
+
echo "$ai_output"
|
|
33
|
+
return 0
|
|
34
|
+
else
|
|
35
|
+
echo "[WARN] Codex failed, falling back to Claude" >&2
|
|
36
|
+
fi
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Fall back to Claude
|
|
40
|
+
echo "[INFO] Using Claude $fallback_model for ideation" >&2
|
|
21
41
|
|
|
22
42
|
# Call Claude and capture output
|
|
23
43
|
local claude_output
|
|
24
|
-
claude_output=$(echo "$prompt" | claude --dangerously-skip-permissions --model "$
|
|
44
|
+
claude_output=$(echo "$prompt" | claude --dangerously-skip-permissions --model "$fallback_model" -p 2>&1)
|
|
25
45
|
local claude_exit_code=$?
|
|
26
46
|
|
|
27
47
|
# Check for usage limit
|
|
@@ -51,6 +71,11 @@ call_claude_with_limit_check() {
|
|
|
51
71
|
return $claude_exit_code
|
|
52
72
|
}
|
|
53
73
|
|
|
74
|
+
# Backward compatibility alias
|
|
75
|
+
call_claude_with_limit_check() {
|
|
76
|
+
call_ai_with_limit_check "$@"
|
|
77
|
+
}
|
|
78
|
+
|
|
54
79
|
# Parse arguments
|
|
55
80
|
use_strategies=true
|
|
56
81
|
no_ai=false
|
|
@@ -253,9 +278,9 @@ ideate_manual() {
|
|
|
253
278
|
|
|
254
279
|
# Generate ideas using AI with multi-strategy approach
|
|
255
280
|
ideate_ai_strategies() {
|
|
256
|
-
# Check for
|
|
257
|
-
if ! command -v claude >/dev/null 2>&1; then
|
|
258
|
-
echo "[WARN]
|
|
281
|
+
# Check for AI CLI (codex or claude)
|
|
282
|
+
if ! command -v codex >/dev/null 2>&1 && ! command -v claude >/dev/null 2>&1; then
|
|
283
|
+
echo "[WARN] No AI CLI found (codex or claude). Falling back to manual entry."
|
|
259
284
|
return 1
|
|
260
285
|
fi
|
|
261
286
|
|
|
@@ -329,9 +354,9 @@ Example descriptions:
|
|
|
329
354
|
|
|
330
355
|
Add exactly $count rows to the CSV file now."
|
|
331
356
|
|
|
332
|
-
echo "[INFO]
|
|
333
|
-
if !
|
|
334
|
-
echo "[WARN]
|
|
357
|
+
echo "[INFO] Generating $count novel exploration ideas..."
|
|
358
|
+
if ! call_ai_with_limit_check "$prompt" "opus"; then
|
|
359
|
+
echo "[WARN] AI failed to generate novel ideas" >&2
|
|
335
360
|
return 1
|
|
336
361
|
fi
|
|
337
362
|
echo "[INFO] Novel exploration ideas generated"
|
|
@@ -382,9 +407,9 @@ Example descriptions:
|
|
|
382
407
|
|
|
383
408
|
Add exactly $count parameter tuning rows to the CSV file now."
|
|
384
409
|
|
|
385
|
-
echo "[INFO]
|
|
386
|
-
if !
|
|
387
|
-
echo "[WARN]
|
|
410
|
+
echo "[INFO] Generating $count hill climbing ideas..."
|
|
411
|
+
if ! call_ai_with_limit_check "$prompt" "opus"; then
|
|
412
|
+
echo "[WARN] AI failed to generate hill climbing ideas" >&2
|
|
388
413
|
return 1
|
|
389
414
|
fi
|
|
390
415
|
echo "[INFO] Hill climbing ideas generated"
|
|
@@ -435,9 +460,9 @@ Example descriptions:
|
|
|
435
460
|
|
|
436
461
|
Add exactly $count structural modification rows to the CSV file now."
|
|
437
462
|
|
|
438
|
-
echo "[INFO]
|
|
439
|
-
if !
|
|
440
|
-
echo "[WARN]
|
|
463
|
+
echo "[INFO] Generating $count structural mutation ideas..."
|
|
464
|
+
if ! call_ai_with_limit_check "$prompt" "opus"; then
|
|
465
|
+
echo "[WARN] AI failed to generate structural mutation ideas" >&2
|
|
441
466
|
return 1
|
|
442
467
|
fi
|
|
443
468
|
echo "[INFO] Structural mutation ideas generated"
|
|
@@ -488,9 +513,9 @@ Example descriptions:
|
|
|
488
513
|
|
|
489
514
|
Add exactly $count hybrid combination rows to the CSV file now."
|
|
490
515
|
|
|
491
|
-
echo "[INFO]
|
|
492
|
-
if !
|
|
493
|
-
echo "[WARN]
|
|
516
|
+
echo "[INFO] Generating $count crossover hybrid ideas..."
|
|
517
|
+
if ! call_ai_with_limit_check "$prompt" "opus"; then
|
|
518
|
+
echo "[WARN] AI failed to generate crossover ideas" >&2
|
|
494
519
|
return 1
|
|
495
520
|
fi
|
|
496
521
|
echo "[INFO] Crossover hybrid ideas generated"
|
|
@@ -498,9 +523,9 @@ Add exactly $count hybrid combination rows to the CSV file now."
|
|
|
498
523
|
|
|
499
524
|
# Legacy AI generation mode (for backward compatibility)
|
|
500
525
|
ideate_ai_legacy() {
|
|
501
|
-
# Check for
|
|
502
|
-
if ! command -v claude >/dev/null 2>&1; then
|
|
503
|
-
echo "[WARN]
|
|
526
|
+
# Check for AI CLI (codex or claude)
|
|
527
|
+
if ! command -v codex >/dev/null 2>&1 && ! command -v claude >/dev/null 2>&1; then
|
|
528
|
+
echo "[WARN] No AI CLI found (codex or claude). Falling back to manual entry."
|
|
504
529
|
return 1
|
|
505
530
|
fi
|
|
506
531
|
|
|
@@ -553,9 +578,9 @@ Requirements for new CSV rows:
|
|
|
553
578
|
|
|
554
579
|
Add exactly $TOTAL_IDEAS algorithm variation rows to the CSV file now."
|
|
555
580
|
|
|
556
|
-
echo "[INFO]
|
|
557
|
-
if !
|
|
558
|
-
echo "[WARN]
|
|
581
|
+
echo "[INFO] Generating $TOTAL_IDEAS ideas (legacy mode)..."
|
|
582
|
+
if ! call_ai_with_limit_check "$prompt" "opus"; then
|
|
583
|
+
echo "[WARN] AI failed to generate ideas" >&2
|
|
559
584
|
return 1
|
|
560
585
|
fi
|
|
561
586
|
echo "[INFO] Legacy ideas generated"
|
|
@@ -71,7 +71,10 @@ reader = csv.reader(sys.stdin)
|
|
|
71
71
|
next(reader) # Skip header
|
|
72
72
|
count = 0
|
|
73
73
|
for row in reader:
|
|
74
|
-
|
|
74
|
+
# If row has fewer than 5 fields, treat as pending
|
|
75
|
+
if len(row) < 5:
|
|
76
|
+
count += 1
|
|
77
|
+
elif len(row) >= 5 and (row[4] == 'pending' or row[4] == ''):
|
|
75
78
|
count += 1
|
|
76
79
|
print(count)
|
|
77
80
|
"
|
package/lib/csv-lock.sh
CHANGED
|
@@ -162,7 +162,15 @@ with open('$csv_file', 'r') as f:
|
|
|
162
162
|
# Find first pending candidate
|
|
163
163
|
candidate_id = None
|
|
164
164
|
for i in range(1, len(rows)):
|
|
165
|
-
|
|
165
|
+
# If row has fewer than 5 fields, it's pending
|
|
166
|
+
if len(rows[i]) < 5:
|
|
167
|
+
candidate_id = rows[i][0]
|
|
168
|
+
# Ensure row has 5 fields before setting status
|
|
169
|
+
while len(rows[i]) < 5:
|
|
170
|
+
rows[i].append('')
|
|
171
|
+
rows[i][4] = 'running' # Update status
|
|
172
|
+
break
|
|
173
|
+
elif len(rows[i]) >= 5 and (rows[i][4] == 'pending' or rows[i][4] == ''):
|
|
166
174
|
candidate_id = rows[i][0]
|
|
167
175
|
rows[i][4] = 'running' # Update status
|
|
168
176
|
break
|
package/lib/csv_helper.py
CHANGED
|
@@ -12,7 +12,11 @@ def find_pending_row(csv_path):
|
|
|
12
12
|
reader = csv.reader(f)
|
|
13
13
|
next(reader) # Skip header
|
|
14
14
|
for row_num, row in enumerate(reader, start=2):
|
|
15
|
-
#
|
|
15
|
+
# If row has fewer than 5 fields, it's pending
|
|
16
|
+
if len(row) < 5:
|
|
17
|
+
return row_num
|
|
18
|
+
|
|
19
|
+
# Ensure row has at least 5 fields for status check
|
|
16
20
|
while len(row) < 5:
|
|
17
21
|
row.append('')
|
|
18
22
|
|