claude-evolve 1.8.5 → 1.8.6
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-run +20 -52
- package/bin/claude-evolve-worker +12 -3
- package/lib/evolution_csv.py +66 -0
- package/package.json +1 -1
package/bin/claude-evolve-run
CHANGED
|
@@ -583,44 +583,17 @@ while true; do
|
|
|
583
583
|
if [[ -f "$FULL_CSV_PATH" && ${#worker_pids[@]} -eq 0 ]]; then
|
|
584
584
|
echo "[DISPATCHER] Checking for stuck candidates (no active workers)..."
|
|
585
585
|
"$PYTHON_CMD" -c "
|
|
586
|
-
import csv
|
|
587
586
|
import sys
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
csv_file = '$FULL_CSV_PATH'
|
|
591
|
-
rows = []
|
|
592
|
-
|
|
593
|
-
with open(csv_file, 'r') as f:
|
|
594
|
-
rows = list(csv.reader(f))
|
|
595
|
-
|
|
596
|
-
reset_count = 0
|
|
597
|
-
has_header = rows and rows[0] and rows[0][0].lower() == 'id'
|
|
598
|
-
start_idx = 1 if has_header else 0
|
|
587
|
+
sys.path.insert(0, '$SCRIPT_DIR/..')
|
|
588
|
+
from lib.evolution_csv import EvolutionCSV
|
|
599
589
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
print(f'[INFO] Resetting stuck running candidate: {candidate_id}', file=sys.stderr)
|
|
608
|
-
rows[i][4] = 'pending'
|
|
609
|
-
reset_count += 1
|
|
610
|
-
# Reset unknown statuses like 'ready'
|
|
611
|
-
elif status not in ['', 'pending', 'complete', 'failed', 'failed-ai-retry',
|
|
612
|
-
'failed-retry1', 'failed-retry2', 'failed-retry3', 'skipped',
|
|
613
|
-
'failed-parent-missing']:
|
|
614
|
-
print(f'[WARN] Resetting unknown status \"{status}\" to pending: {candidate_id}', file=sys.stderr)
|
|
615
|
-
rows[i][4] = 'pending'
|
|
616
|
-
reset_count += 1
|
|
617
|
-
|
|
618
|
-
if reset_count > 0:
|
|
619
|
-
with open(csv_file + '.tmp', 'w', newline='') as f:
|
|
620
|
-
csv.writer(f).writerows(rows)
|
|
621
|
-
Path(csv_file + '.tmp').rename(csv_file)
|
|
622
|
-
print(f'[INFO] Reset {reset_count} stuck/unknown candidates to pending', file=sys.stderr)
|
|
623
|
-
" || true
|
|
590
|
+
try:
|
|
591
|
+
with EvolutionCSV('$FULL_CSV_PATH') as csv:
|
|
592
|
+
reset_count = csv.reset_stuck_candidates()
|
|
593
|
+
except Exception as e:
|
|
594
|
+
print(f'[ERROR] Failed to reset stuck candidates: {e}', file=sys.stderr)
|
|
595
|
+
sys.exit(1)
|
|
596
|
+
"
|
|
624
597
|
fi
|
|
625
598
|
fi
|
|
626
599
|
|
|
@@ -642,22 +615,17 @@ if reset_count > 0:
|
|
|
642
615
|
# Before blocking, do final check for stuck work (immediate, not periodic)
|
|
643
616
|
echo "[DISPATCHER] Performing final verification for stuck candidates..."
|
|
644
617
|
stuck_work_count=$("$PYTHON_CMD" -c "
|
|
645
|
-
import
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
(status and status not in ['', 'pending', 'complete', 'failed', 'skipped',
|
|
657
|
-
'failed-ai-retry', 'failed-retry1', 'failed-retry2', 'failed-retry3',
|
|
658
|
-
'failed-parent-missing']):
|
|
659
|
-
stuck += 1
|
|
660
|
-
print(stuck)
|
|
618
|
+
import sys
|
|
619
|
+
sys.path.insert(0, '$SCRIPT_DIR/..')
|
|
620
|
+
from lib.evolution_csv import EvolutionCSV
|
|
621
|
+
|
|
622
|
+
try:
|
|
623
|
+
with EvolutionCSV('$FULL_CSV_PATH') as csv:
|
|
624
|
+
stuck = csv.count_stuck_candidates()
|
|
625
|
+
print(stuck)
|
|
626
|
+
except Exception as e:
|
|
627
|
+
print(f'[ERROR] Failed to count stuck candidates: {e}', file=sys.stderr)
|
|
628
|
+
print('0') # Default to 0 on error
|
|
661
629
|
" 2>/dev/null || echo "0")
|
|
662
630
|
|
|
663
631
|
if [[ $stuck_work_count -gt 0 ]]; then
|
package/bin/claude-evolve-worker
CHANGED
|
@@ -297,13 +297,22 @@ with EvolutionCSV('$FULL_CSV_PATH') as csv:
|
|
|
297
297
|
if [[ "$current_status" == "complete" ]]; then
|
|
298
298
|
echo "[WORKER-$$] Already evaluated - skipping"
|
|
299
299
|
# Reset status back to complete since get_next_pending_candidate() set it to running
|
|
300
|
-
"$PYTHON_CMD" -c "
|
|
300
|
+
if ! "$PYTHON_CMD" -c "
|
|
301
301
|
import sys
|
|
302
302
|
sys.path.insert(0, '$SCRIPT_DIR/..')
|
|
303
303
|
from lib.evolution_csv import EvolutionCSV
|
|
304
304
|
with EvolutionCSV('$FULL_CSV_PATH') as csv:
|
|
305
|
-
csv.update_candidate_status('$candidate_id', 'complete')
|
|
306
|
-
|
|
305
|
+
success = csv.update_candidate_status('$candidate_id', 'complete')
|
|
306
|
+
if not success:
|
|
307
|
+
print(f'ERROR: Failed to update status for $candidate_id', file=sys.stderr)
|
|
308
|
+
sys.exit(1)
|
|
309
|
+
"; then
|
|
310
|
+
echo "[WORKER-$$] ERROR: Failed to reset status to complete for $candidate_id" >&2
|
|
311
|
+
# Don't clear CURRENT_CANDIDATE_ID so cleanup handler can try again
|
|
312
|
+
return 1
|
|
313
|
+
fi
|
|
314
|
+
|
|
315
|
+
echo "[WORKER-$$] Status confirmed as complete"
|
|
307
316
|
# Clear CURRENT_CANDIDATE_ID before returning to prevent cleanup from interfering
|
|
308
317
|
CURRENT_CANDIDATE_ID=""
|
|
309
318
|
return 0
|
package/lib/evolution_csv.py
CHANGED
|
@@ -416,6 +416,72 @@ class EvolutionCSV:
|
|
|
416
416
|
|
|
417
417
|
return fixed_count
|
|
418
418
|
|
|
419
|
+
def reset_stuck_candidates(self) -> int:
|
|
420
|
+
"""
|
|
421
|
+
Reset 'running' candidates and unknown statuses to 'pending'.
|
|
422
|
+
Should only be called when no workers are active.
|
|
423
|
+
Returns the number of candidates reset.
|
|
424
|
+
"""
|
|
425
|
+
rows = self._read_csv()
|
|
426
|
+
if not rows:
|
|
427
|
+
return 0
|
|
428
|
+
|
|
429
|
+
reset_count = 0
|
|
430
|
+
has_header = rows and rows[0] and rows[0][0].lower() == 'id'
|
|
431
|
+
start_idx = 1 if has_header else 0
|
|
432
|
+
|
|
433
|
+
valid_statuses = {'', 'pending', 'complete', 'failed', 'failed-ai-retry',
|
|
434
|
+
'failed-retry1', 'failed-retry2', 'failed-retry3', 'skipped',
|
|
435
|
+
'failed-parent-missing', 'running'}
|
|
436
|
+
|
|
437
|
+
for i in range(start_idx, len(rows)):
|
|
438
|
+
if len(rows[i]) > 4:
|
|
439
|
+
status = rows[i][4].strip() if rows[i][4] else ''
|
|
440
|
+
candidate_id = rows[i][0] if rows[i] else ''
|
|
441
|
+
|
|
442
|
+
# Reset 'running' when no workers are active
|
|
443
|
+
if status == 'running':
|
|
444
|
+
print(f'[INFO] Resetting stuck running candidate: {candidate_id}', file=sys.stderr)
|
|
445
|
+
rows[i][4] = 'pending'
|
|
446
|
+
reset_count += 1
|
|
447
|
+
# Reset unknown statuses
|
|
448
|
+
elif status not in valid_statuses:
|
|
449
|
+
print(f'[WARN] Resetting unknown status "{status}" to pending: {candidate_id}', file=sys.stderr)
|
|
450
|
+
rows[i][4] = 'pending'
|
|
451
|
+
reset_count += 1
|
|
452
|
+
|
|
453
|
+
if reset_count > 0:
|
|
454
|
+
self._write_csv(rows)
|
|
455
|
+
print(f'[INFO] Reset {reset_count} stuck/unknown candidates to pending', file=sys.stderr)
|
|
456
|
+
|
|
457
|
+
return reset_count
|
|
458
|
+
|
|
459
|
+
def count_stuck_candidates(self) -> int:
|
|
460
|
+
"""
|
|
461
|
+
Count candidates that are stuck (running or have unknown status).
|
|
462
|
+
Returns the number of stuck candidates.
|
|
463
|
+
"""
|
|
464
|
+
rows = self._read_csv()
|
|
465
|
+
if not rows:
|
|
466
|
+
return 0
|
|
467
|
+
|
|
468
|
+
stuck = 0
|
|
469
|
+
has_header = rows and rows[0] and rows[0][0].lower() == 'id'
|
|
470
|
+
start_idx = 1 if has_header else 0
|
|
471
|
+
|
|
472
|
+
valid_statuses = {'', 'pending', 'complete', 'failed', 'failed-ai-retry',
|
|
473
|
+
'failed-retry1', 'failed-retry2', 'failed-retry3', 'skipped',
|
|
474
|
+
'failed-parent-missing'}
|
|
475
|
+
|
|
476
|
+
for i in range(start_idx, len(rows)):
|
|
477
|
+
if len(rows[i]) > 4:
|
|
478
|
+
status = rows[i][4].strip() if rows[i][4] else ''
|
|
479
|
+
# Count running and unknown statuses
|
|
480
|
+
if status == 'running' or (status and status not in valid_statuses):
|
|
481
|
+
stuck += 1
|
|
482
|
+
|
|
483
|
+
return stuck
|
|
484
|
+
|
|
419
485
|
def has_pending_work(self) -> bool:
|
|
420
486
|
"""Check if there are any pending candidates. Used by dispatcher."""
|
|
421
487
|
return self.count_pending_candidates() > 0
|