From da1aed34c66e42d25b4403f04d414e0bf2329939 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 21 Jan 2026 21:31:50 -0700 Subject: [PATCH] fix(analyzer,monitor): EXIT_SIGNAL detection and monitor paths (v0.10.1) Fixes #113 - EXIT_SIGNAL not detected in JSON output format Fixes #117 - ralph_monitor.sh uses wrong paths after v0.10.0 migration Bug fixes: - Parse EXIT_SIGNAL from .result field when Claude CLI returns JSON format - Add safety circuit breaker: force exit after 5 consecutive completion indicators - Fix checkbox parsing for indented markdown with POSIX [[:space:]]* pattern - Update ralph_monitor.sh paths: status.json, logs/ralph.log, progress.json Files changed: - lib/response_analyzer.sh: Extract RALPH_STATUS from embedded .result text - ralph_loop.sh: Safety circuit breaker + indented checkbox patterns - ralph_monitor.sh: All paths updated for .ralph/ subfolder - create_files.sh: Indented checkbox pattern consistency - README.md: v0.10.1 changelog, version bump, test count update - CLAUDE.md: Version bump to v0.10.1 All 310 tests pass. --- CLAUDE.md | 2 +- README.md | 28 ++++++++++++++++++++++------ create_files.sh | 5 +++-- lib/response_analyzer.sh | 21 +++++++++++++++++++++ ralph_loop.sh | 22 +++++++++++++++++----- ralph_monitor.sh | 8 ++++---- 6 files changed, 68 insertions(+), 18 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0dc6971..ab5d2b3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co This is the Ralph for Claude Code repository - an autonomous AI development loop system that enables continuous development cycles with intelligent exit detection and rate limiting. -**Version**: v0.10.0 | **Tests**: 310 passing (100% pass rate) | **CI/CD**: GitHub Actions +**Version**: v0.10.1 | **Tests**: 310 passing (100% pass rate) | **CI/CD**: GitHub Actions ## Core Architecture diff --git a/README.md b/README.md index 4681324..623e070 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![CI](https://github.com/frankbria/ralph-claude-code/actions/workflows/test.yml/badge.svg)](https://github.com/frankbria/ralph-claude-code/actions/workflows/test.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-0.10.0-blue) -![Tests](https://img.shields.io/badge/tests-308%20passing-green) +![Version](https://img.shields.io/badge/version-0.10.1-blue) +![Tests](https://img.shields.io/badge/tests-310%20passing-green) [![GitHub Issues](https://img.shields.io/github/issues/frankbria/ralph-claude-code)](https://github.com/frankbria/ralph-claude-code/issues) [![Mentioned in Awesome Claude Code](https://awesome.re/mentioned-badge.svg)](https://github.com/hesreallyhim/awesome-claude-code) [![Follow on X](https://img.shields.io/twitter/follow/FrankBria18044?style=social)](https://x.com/FrankBria18044) @@ -16,9 +16,9 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t ## Project Status -**Version**: v0.10.0 - Active Development +**Version**: v0.10.1 - Active Development **Core Features**: Working and tested -**Test Coverage**: 308 tests, 100% pass rate +**Test Coverage**: 310 tests, 100% pass rate ### What's Working Now - Autonomous development loops with intelligent exit detection @@ -36,10 +36,26 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t - PRD import functionality - **CI/CD pipeline with GitHub Actions** - **Dedicated uninstall script for clean removal** -- 308 passing tests across 11 test files +- 310 passing tests across 11 test files ### Recent Improvements +**v0.10.1 - Bug Fixes & Monitor Path Corrections** +- Fixed `ralph_monitor.sh` hardcoded paths for v0.10.0 compatibility: + - `STATUS_FILE`: `status.json` → `.ralph/status.json` + - `LOG_FILE`: `logs/ralph.log` → `.ralph/logs/ralph.log` + - `progress.json` → `.ralph/progress.json` +- Fixed EXIT_SIGNAL parsing in JSON format (Bug #1): + - Now extracts `EXIT_SIGNAL` from `.result` field when Claude CLI returns JSON + - Properly detects RALPH_STATUS blocks embedded in JSON response text +- Added safety circuit breaker (Bug #2): + - Force exit after 5 consecutive completion indicators (prevents infinite loops) + - Higher threshold than normal (2) to avoid false positives while preventing API waste +- Fixed checkbox parsing for indented markdown (Bug #3): + - Changed patterns from `^- \[` to `^[[:space:]]*- \[` (POSIX-compliant) + - Supports indented checkboxes in `@fix_plan.md` +- Updated README.md documentation example for new log path + **v0.10.0 - .ralph/ Subfolder Structure (BREAKING CHANGE)** - **Breaking**: Moved all Ralph-specific files to `.ralph/` subfolder - Project root stays clean: only `src/`, `README.md`, and user files remain @@ -584,7 +600,7 @@ Shows real-time: ralph --status # Manual log inspection -tail -f logs/ralph.log +tail -f .ralph/logs/ralph.log ``` ### Common Issues diff --git a/create_files.sh b/create_files.sh index 26ca9fd..4028c55 100755 --- a/create_files.sh +++ b/create_files.sh @@ -204,9 +204,10 @@ should_exit_gracefully() { fi # 4. Check fix_plan.md for completion + # Bug #3 Fix: Support indented markdown checkboxes with [[:space:]]* pattern if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then - local total_items=$(grep -c "^- \[" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0") - local completed_items=$(grep -c "^- \[x\]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0") + local total_items=$(grep -cE "^[[:space:]]*- \[" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0") + local completed_items=$(grep -cE "^[[:space:]]*- \[x\]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0") if [[ $total_items -gt 0 ]] && [[ $completed_items -eq $total_items ]]; then log_status "WARN" "Exit condition: All fix_plan.md items completed ($completed_items/$total_items)" diff --git a/lib/response_analyzer.sh b/lib/response_analyzer.sh index caf814b..a165f8b 100644 --- a/lib/response_analyzer.sh +++ b/lib/response_analyzer.sh @@ -89,6 +89,27 @@ parse_json_response() { # Exit signal: from flat format OR derived from completion_status local exit_signal=$(jq -r '.exit_signal // false' "$output_file" 2>/dev/null) + # Bug #1 Fix: If exit_signal is still false, check for RALPH_STATUS block in .result field + # Claude CLI JSON format embeds the RALPH_STATUS block within the .result text field + if [[ "$exit_signal" == "false" && "$has_result_field" == "true" ]]; then + local result_text=$(jq -r '.result // ""' "$output_file" 2>/dev/null) + if [[ -n "$result_text" ]] && echo "$result_text" | grep -q -- "---RALPH_STATUS---"; then + # Extract EXIT_SIGNAL value from RALPH_STATUS block within result text + local embedded_exit_sig=$(echo "$result_text" | grep "EXIT_SIGNAL:" | cut -d: -f2 | xargs) + if [[ "$embedded_exit_sig" == "true" ]]; then + exit_signal="true" + [[ "${VERBOSE_PROGRESS:-}" == "true" ]] && echo "DEBUG: Extracted EXIT_SIGNAL=true from .result RALPH_STATUS block" >&2 + fi + # Also check STATUS field as fallback + local embedded_status=$(echo "$result_text" | grep "STATUS:" | cut -d: -f2 | xargs) + if [[ "$embedded_status" == "COMPLETE" && "$exit_signal" != "true" ]]; then + # STATUS: COMPLETE without explicit EXIT_SIGNAL implies completion + exit_signal="true" + [[ "${VERBOSE_PROGRESS:-}" == "true" ]] && echo "DEBUG: Inferred EXIT_SIGNAL=true from .result STATUS=COMPLETE" >&2 + fi + fi + fi + # Work type: from flat format local work_type=$(jq -r '.work_type // "UNKNOWN"' "$output_file" 2>/dev/null) diff --git a/ralph_loop.sh b/ralph_loop.sh index 3e48d38..bff890f 100755 --- a/ralph_loop.sh +++ b/ralph_loop.sh @@ -313,7 +313,17 @@ should_exit_gracefully() { return 0 fi - # 3. Strong completion indicators (only if Claude's EXIT_SIGNAL is true) + # 3. Safety circuit breaker - force exit after 5 consecutive completion indicators + # Bug #2 Fix: Prevents infinite loops when EXIT_SIGNAL is not explicitly set + # but completion patterns clearly indicate work is done. Threshold of 5 is higher + # than normal threshold (2) to avoid false positives while preventing API waste. + if [[ $recent_completion_indicators -ge 5 ]]; then + log_status "WARN" "🚨 SAFETY CIRCUIT BREAKER: Force exit after 5 consecutive completion indicators ($recent_completion_indicators)" >&2 + echo "safety_circuit_breaker" + return 0 + fi + + # 4. Strong completion indicators (only if Claude's EXIT_SIGNAL is true) # This prevents premature exits when heuristics detect completion patterns # but Claude explicitly indicates work is still in progress via RALPH_STATUS block. # The exit_signal in .response_analysis represents Claude's explicit intent. @@ -330,10 +340,11 @@ should_exit_gracefully() { log_status "INFO" "DEBUG: Completion indicators ($recent_completion_indicators) present but EXIT_SIGNAL=false, continuing..." >&2 fi - # 4. Check fix_plan.md for completion + # 5. Check fix_plan.md for completion + # Bug #3 Fix: Support indented markdown checkboxes with [[:space:]]* pattern if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then - local total_items=$(grep -c "^- \[" "$RALPH_DIR/@fix_plan.md" 2>/dev/null) - local completed_items=$(grep -c "^- \[x\]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null) + local total_items=$(grep -cE "^[[:space:]]*- \[" "$RALPH_DIR/@fix_plan.md" 2>/dev/null) + local completed_items=$(grep -cE "^[[:space:]]*- \[x\]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null) # Handle case where grep returns no matches (exit code 1) [[ -z "$total_items" ]] && total_items=0 @@ -445,8 +456,9 @@ build_loop_context() { context="Loop #${loop_count}. " # Extract incomplete tasks from @fix_plan.md + # Bug #3 Fix: Support indented markdown checkboxes with [[:space:]]* pattern if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then - local incomplete_tasks=$(grep -c "^- \[ \]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0") + local incomplete_tasks=$(grep -cE "^[[:space:]]*- \[ \]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0") context+="Remaining tasks: ${incomplete_tasks}. " fi diff --git a/ralph_monitor.sh b/ralph_monitor.sh index 767fcb2..3bf76cc 100755 --- a/ralph_monitor.sh +++ b/ralph_monitor.sh @@ -3,8 +3,8 @@ # Ralph Status Monitor - Live terminal dashboard for the Ralph loop set -e -STATUS_FILE="status.json" -LOG_FILE="logs/ralph.log" +STATUS_FILE=".ralph/status.json" +LOG_FILE=".ralph/logs/ralph.log" REFRESH_INTERVAL=2 # Colors @@ -74,8 +74,8 @@ display_status() { fi # Claude Code Progress section - if [[ -f "progress.json" ]]; then - local progress_data=$(cat "progress.json" 2>/dev/null) + if [[ -f ".ralph/progress.json" ]]; then + local progress_data=$(cat ".ralph/progress.json" 2>/dev/null) local progress_status=$(echo "$progress_data" | jq -r '.status // "idle"' 2>/dev/null || echo "idle") if [[ "$progress_status" == "executing" ]]; then