diff --git a/ralph_loop.sh b/ralph_loop.sh index 5292976..84a7e78 100755 --- a/ralph_loop.sh +++ b/ralph_loop.sh @@ -1279,6 +1279,8 @@ execute_claude_code() { log_status "WARN" "Could not find result message in stream output" # Keep stream output as-is for debugging fi + # Clean up temporary stream log after extraction + rm -f "$stream_output_file" fi else # BACKGROUND MODE: Original behavior with progress monitoring @@ -1314,6 +1316,7 @@ execute_claude_code() { # Get PID and monitor progress local claude_pid=$! + _CLAUDE_PID=$claude_pid local progress_counter=0 # Early failure detection: if the command doesn't exist or fails immediately, @@ -1389,6 +1392,7 @@ EOF # Wait for the process to finish and get exit code wait $claude_pid exit_code=$? + _CLAUDE_PID="" fi if [ $exit_code -eq 0 ]; then @@ -1518,17 +1522,35 @@ EOF # Cleanup function cleanup() { - log_status "INFO" "Ralph loop interrupted. Cleaning up..." - reset_session "manual_interrupt" - update_status "$loop_count" "$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0")" "interrupted" "stopped" - exit 0 + local trap_exit_code=${1:-$?} + # Reentrancy guard — prevent double execution from EXIT + signal combination + if [[ "$_CLEANUP_DONE" == "true" ]]; then return; fi + _CLEANUP_DONE=true + + # Kill background Claude process if still running + if [[ -n "$_CLAUDE_PID" ]] && kill -0 "$_CLAUDE_PID" 2>/dev/null; then + kill "$_CLAUDE_PID" 2>/dev/null || true + wait "$_CLAUDE_PID" 2>/dev/null || true + fi + _CLAUDE_PID="" + + # Only record "interrupted" status for abnormal exits (non-zero exit code) + # Normal exit (code 0) preserves the status already written by the main loop + if [[ $loop_count -gt 0 && $trap_exit_code -ne 0 ]]; then + log_status "INFO" "Ralph loop interrupted. Cleaning up..." + reset_session "manual_interrupt" + update_status "$loop_count" "$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0")" "interrupted" "stopped" + fi + # No exit here — EXIT trap handles natural termination } -# Set up signal handlers -trap cleanup SIGINT SIGTERM +# Set up signal handlers — EXIT covers SIGINT, SIGTERM, set -e errors, and normal exit +trap 'cleanup $?' EXIT -# Global variable for loop count (needed by cleanup function) +# Global variables for cleanup loop_count=0 +_CLAUDE_PID="" +_CLEANUP_DONE=false # Main loop main() { @@ -1600,6 +1622,10 @@ main() { # Initialize session tracking before entering the loop init_session_tracking + # Reset stale exit signals from previous session (SIGKILL/OOM recovery) + echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE" + rm -f "$RALPH_DIR/.response_analysis" + log_status "INFO" "Starting main loop..." while true; do diff --git a/tests/unit/test_session_continuity.bats b/tests/unit/test_session_continuity.bats index 6236ff8..539b8e0 100644 --- a/tests/unit/test_session_continuity.bats +++ b/tests/unit/test_session_continuity.bats @@ -148,7 +148,7 @@ function_exists_in_ralph() { @test "cleanup function includes session reset" { # Check if cleanup function includes reset_session - run grep -A5 'cleanup()' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" + run grep -A20 'cleanup()' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" [[ "$output" == *"reset_session"* ]] || skip "Cleanup session reset not yet implemented" }