Skip to content

fix(loop): respect Claude's EXIT_SIGNAL when checking completion indicators#90

Merged
frankbria merged 3 commits into
mainfrom
fix/exit-signal-respect
Jan 13, 2026
Merged

fix(loop): respect Claude's EXIT_SIGNAL when checking completion indicators#90
frankbria merged 3 commits into
mainfrom
fix/exit-signal-respect

Conversation

@frankbria
Copy link
Copy Markdown
Owner

@frankbria frankbria commented Jan 13, 2026

Summary

  • Fixed premature exit bug where should_exit_gracefully() exited based solely on completion indicator heuristics, ignoring Claude's explicit EXIT_SIGNAL: false in the RALPH_STATUS block
  • Added dual-condition check requiring BOTH completion_indicators >= 2 AND exit_signal == true
  • Fixed response_analyzer.sh to respect explicit EXIT_SIGNAL and skip heuristic override

Decision Matrix

completion_indicators >= 2 EXIT_SIGNAL .response_analysis exists Result
true Yes Exit (project_complete)
false Yes Continue
N/A No Continue (defaults to false)
true Yes Continue (threshold not met)

Test plan

  • Added 10 new unit tests for EXIT_SIGNAL behavior (tests 21-30 in test_exit_detection.bats)
  • Added 4 new integration tests for edge cases (tests 21-24 in test_edge_cases.bats)
  • All 306 tests pass
  • Verified backward compatibility with existing exit conditions (test_saturation, done_signals, plan_complete)

Summary by CodeRabbit

Release Notes

  • New Features

    • Exit behavior now requires an explicit completion signal from Claude, providing more precise control over project termination.
  • Bug Fixes

    • Improved handling of conflicting signals and edge cases in exit detection.
    • Added robustness for missing or invalid analysis files.
  • Tests

    • Expanded test coverage for completion signal scenarios, edge cases, and multi-state transitions.
  • Documentation

    • Updated documentation on completion detection and exit mechanisms.

✏️ Tip: You can customize this high-level summary in your review settings.

…cators

The should_exit_gracefully() function was exiting prematurely based solely
on completion_indicators heuristics, ignoring Claude's explicit EXIT_SIGNAL
in the RALPH_STATUS block. This caused premature exits during productive
iterations when Claude reported work in progress.

Changes:
- ralph_loop.sh: Added dual-condition check requiring BOTH completion
  indicators >= 2 AND exit_signal == true before exiting
- response_analyzer.sh: Added explicit_exit_signal_found flag to prevent
  natural language heuristics from overriding Claude's explicit intent
- Added 14 new tests (10 unit + 4 integration) covering EXIT_SIGNAL behavior

Decision matrix:
| indicators >= 2 | EXIT_SIGNAL | Result |
|-----------------|-------------|--------|
| true            | true        | Exit   |
| true            | false       | Continue |
| true            | missing     | Continue (defaults to false) |
| false           | true        | Continue (threshold not met) |

Fixes premature exit bug during productive development iterations.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 13, 2026

Walkthrough

Ralph introduces a dual-condition exit gate requiring both recent completion indicators (≥2) and an explicit EXIT_SIGNAL (true) from Claude to trigger project completion. EXIT_SIGNAL now takes precedence over heuristics and completion status, with new logic in response_analyzer.sh to flag and respect explicit signals, and changes to ralph_loop.sh to enforce the gate.

Changes

Cohort / File(s) Summary
Core Logic - Exit Signal Handling
lib/response_analyzer.sh, ralph_loop.sh
Adds explicit_exit_signal_found flag in response_analyzer.sh to mark when EXIT_SIGNAL is provided in RALPH_STATUS; sets confidence_score to 100 for explicit signals. Gates completion exit in ralph_loop.sh to require both recent_completion_indicators ≥ 2 AND EXIT_SIGNAL == true, bypassing heuristics when explicit signal is detected.
Documentation
CLAUDE.md
Documents the dual-condition exit gate mechanism with a matrix of outcomes based on EXIT_SIGNAL presence/value, conflict resolution rule tying EXIT_SIGNAL to project completion, and implementation snippet showing the gating logic in ralph_loop.sh.
Test Coverage - Edge Cases
tests/integration/test_edge_cases.bats
Adds Edge Cases 21–25 covering EXIT_SIGNAL false persistence across loops, STATUS/EXIT_SIGNAL transitions, mid-loop .response_analysis deletion and recreation, conflicting signals (COMPLETE but EXIT_SIGNAL false), and JSON-formatted responses with EXIT_SIGNAL variants.
Test Coverage - Unit Tests
tests/unit/test_exit_detection.bats
Expands test setup with RESPONSE_ANALYSIS_FILE export; adds/updates tests 8, 21–31 to validate EXIT_SIGNAL interaction, missing/invalid files, explicit/absent fields, and precedence with test_saturation and done_signals.

Sequence Diagram

sequenceDiagram
    participant Claude as Claude<br/>(AI Assistant)
    participant RA as response_analyzer.sh
    participant RL as ralph_loop.sh
    participant State as .response_analysis<br/>(State File)
    
    Claude->>RA: Output with RALPH_STATUS + EXIT_SIGNAL
    activate RA
    RA->>RA: Check if EXIT_SIGNAL provided
    alt EXIT_SIGNAL Present
        RA->>RA: Set explicit_exit_signal_found = true
        RA->>RA: Set confidence_score = 100
        RA->>State: Write exit_signal (true/false)<br/>with high confidence
    else EXIT_SIGNAL Absent
        RA->>RA: Apply heuristics<br/>(completion keywords, loops, etc.)
        RA->>State: Write heuristic-derived exit_signal
    end
    deactivate RA
    
    RL->>State: Read exit_signal & recent_completion_indicators
    activate RL
    alt exit_signal == true AND indicators >= 2
        RL->>RL: Exit as project_complete
    else exit_signal == false
        RL->>RL: Continue loop (ignore indicators)
    else indicators < 2
        RL->>RL: Continue loop
    end
    deactivate RL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 A signal hops through data streams,
Claude's intent flows clear—
No heuristic guesswork here!
Explicit gates the exit dreams,
Precedence reigns supreme,
Project completion crisp and dear! 🎯

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: respecting Claude's EXIT_SIGNAL when checking completion indicators, which is the central fix across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/exit-signal-respect

Comment @coderabbitai help to get the list of available commands and usage tips.

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Jan 13, 2026

Gate loop exit on Claude's EXIT_SIGNAL:true and require recent_completion_indicators >= 2 in ralph_loop.sh

Add explicit EXIT_SIGNAL parsing in lib/response_analyzer.sh and require it for completion-based exits in ralph_loop.sh; update tests to cover false, true, missing, and conflict cases; expand documentation in CLAUDE.md.

📍Where to Start

Start with should_exit_gracefully in ralph_loop.sh, then review analyze_response in lib/response_analyzer.sh.


Macroscope summarized 2b3fc83.

Comment thread lib/response_analyzer.sh Outdated
@claude
Copy link
Copy Markdown

claude Bot commented Jan 13, 2026

Code Review - EXIT_SIGNAL Respect Fix

This PR addresses a critical bug in Ralph's exit detection logic. The implementation is well-thought-out and properly tested. Here's my detailed review:


Strengths

  1. Clear Problem Identification: The fix correctly identifies that completion heuristics were overriding Claude's explicit EXIT_SIGNAL, causing premature loop exits during productive work.

  2. Excellent Test Coverage:

    • 10 new unit tests (tests 21-30 in test_exit_detection.bats)
    • 4 new integration tests (tests 21-24 in test_edge_cases.bats)
    • Tests cover edge cases: missing files, malformed JSON, priority ordering, transitions
    • Test count: 306 total (100% pass rate claimed in PR description)
  3. Dual-Condition Logic: The fix implements a proper AND condition requiring BOTH completion_indicators >= 2 AND exit_signal == true, which is the correct approach.

  4. Backward Compatibility: Defaults to exit_signal=false when .response_analysis is missing or malformed, ensuring safe fallback behavior.

  5. Documentation: Excellent inline comments explaining the rationale (lines 312-315 in ralph_loop.sh, lines 300-302 and 418-420 in response_analyzer.sh).


🔍 Code Quality Issues

1. Logic Inconsistency in response_analyzer.sh (Line 313)

Location: lib/response_analyzer.sh:313

if [[ "$exit_sig" == "true" || "$status" == "COMPLETE" ]]; then

Issue: This line creates an OR condition where STATUS: COMPLETE can set exit_signal=true even when EXIT_SIGNAL: false is explicitly provided. This defeats the purpose of the fix.

@claude
Copy link
Copy Markdown

claude Bot commented Jan 13, 2026

Example scenario:

---RALPH_STATUS---
STATUS: COMPLETE
EXIT_SIGNAL: false
---END_RALPH_STATUS---

With current code:

  • Line 311: explicit_exit_signal_found=true (correct)
  • Line 313: Condition evaluates to true because STATUS == COMPLETE
  • Result: exit_signal=true (WRONG - should respect EXIT_SIGNAL: false)

Recommendation: Change line 313 to:

if [[ "$exit_sig" == "true" ]]; then

And handle STATUS: COMPLETE only in the elif block (line 321) when EXIT_SIGNAL is not provided.


2. Missing Test for STATUS/EXIT_SIGNAL Conflict

Issue: No test validates the scenario where STATUS: COMPLETE but EXIT_SIGNAL: false. This is a critical edge case that should be covered.

Recommendation: Add a test to verify EXIT_SIGNAL takes precedence over STATUS when both are present.

@claude
Copy link
Copy Markdown

claude Bot commented Jan 13, 2026

3. Error Handling Enhancement Opportunity

Location: ralph_loop.sh:318

The current code handles JSON parsing errors gracefully but silently swallows all errors, including file permission issues. Consider adding logging when jq fails to aid debugging.


🛡️ Security & Performance

  • No security concerns: The fix only affects control flow logic
  • No performance impact: Single file read with jq parse per loop iteration
  • Safe defaults: Fallback to false prevents unintended exits

📋 Decision Matrix Verification

The PR's decision matrix is correct in principle, but due to Issue Number 1 above (the OR condition with STATUS), there's an undocumented edge case where completion_indicators + STATUS=COMPLETE + EXIT_SIGNAL=false currently exits when it should continue.

@claude
Copy link
Copy Markdown

claude Bot commented Jan 13, 2026

🎯 Recommendations

Must Fix Before Merge:

  1. Fix the OR condition in response_analyzer.sh:313 to respect explicit EXIT_SIGNAL
  2. Add test case for STATUS=COMPLETE + EXIT_SIGNAL=false conflict

Nice to Have:

  1. Add warning log when jq parsing fails
  2. Update CLAUDE.md with the new EXIT_SIGNAL behavior in the "Exit Conditions" section
  3. Consider adding a debug log in response_analyzer.sh when explicit_exit_signal_found=true

Overall Assessment

This is a well-designed fix with excellent test coverage that solves a real problem. The implementation demonstrates strong understanding of the exit detection flow and proper separation of concerns between heuristic analysis and explicit signals.

The logic inconsistency in Issue Number 1 is critical but easy to fix. Once addressed, this PR will significantly improve Ralph's reliability during autonomous development loops.

Status: Approve pending fixes to Issue Number 1 and Issue Number 2.


Great work on identifying this bug and implementing a comprehensive solution! 🎉

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @ralph_loop.sh:
- Around line 312-327: Update CLAUDE.md to reflect the new exit gating: document
that intelligent exit detection now requires both recent_completion_indicators
>= 2 and Claude's explicit EXIT_SIGNAL=true from the RALPH_STATUS block (read
from .response_analysis into claude_exit_signal) before the script returns
"project_complete"; specifically update the "Intelligent Exit Detection" and
"Exit Conditions and Thresholds" sections to describe the combined condition,
where EXIT_SIGNAL is the value in the .response_analysis JSON at
.analysis.exit_signal and explain the rationale and example behavior when
EXIT_SIGNAL is false even if completion indicators are high.
🧹 Nitpick comments (1)
lib/response_analyzer.sh (1)

307-308: Declare and assign separately to avoid masking return values.

Per static analysis (SC2155), combining local declaration with command substitution masks the exit status of grep. If grep fails (no match), the error is silently ignored.

♻️ Proposed fix
-        local status=$(grep "STATUS:" "$output_file" | cut -d: -f2 | xargs)
-        local exit_sig=$(grep "EXIT_SIGNAL:" "$output_file" | cut -d: -f2 | xargs)
+        local status
+        status=$(grep "STATUS:" "$output_file" | cut -d: -f2 | xargs) || status=""
+        local exit_sig
+        exit_sig=$(grep "EXIT_SIGNAL:" "$output_file" | cut -d: -f2 | xargs) || exit_sig=""
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e8d3fd and eb201ae.

📒 Files selected for processing (4)
  • lib/response_analyzer.sh
  • ralph_loop.sh
  • tests/integration/test_edge_cases.bats
  • tests/unit/test_exit_detection.bats
🧰 Additional context used
📓 Path-based instructions (4)
**/*.sh

📄 CodeRabbit inference engine (CLAUDE.md)

All bash scripts in Ralph must use array-based command building for CLI arguments to maintain shell injection safety

Files:

  • ralph_loop.sh
  • lib/response_analyzer.sh
ralph_loop.sh

📄 CodeRabbit inference engine (CLAUDE.md)

Ralph loop context injection via build_loop_context() must include: loop number, remaining tasks from @fix_plan.md, circuit breaker state, and previous work summary

Files:

  • ralph_loop.sh
**/response_analyzer.sh

📄 CodeRabbit inference engine (CLAUDE.md)

**/response_analyzer.sh: Use JSON output format detection and parsing in Ralph scripts - implement detect_output_format() and parse_json_response() functions for Claude CLI integration
Use session management functions (store_session_id, get_last_session_id, should_resume_session) for maintaining context across Ralph loop iterations with 24-hour expiration

Files:

  • lib/response_analyzer.sh
tests/**/*.bats

📄 CodeRabbit inference engine (CLAUDE.md)

Test files must follow naming convention: test_*.bats in tests/unit/ and tests/integration/ directories, organized by component (cli_parsing, json_parsing, session_continuity, exit_detection, etc.)

Files:

  • tests/unit/test_exit_detection.bats
  • tests/integration/test_edge_cases.bats
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Implementation documentation in CLAUDE.md must remain synchronized with codebase - update when: new commands added to 'Key Commands', exit conditions change, installation instructions change, new Ralph loop behaviors introduced
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to ralph_loop.sh : Ralph loop context injection via build_loop_context() must include: loop number, remaining tasks from fix_plan.md, circuit breaker state, and previous work summary
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh : Use JSON output format detection and parsing in Ralph scripts - implement `detect_output_format()` and `parse_json_response()` functions for Claude CLI integration
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|**/ralph_import.sh : Implement JSON field extraction from Claude Code CLI responses supporting both flat format and Claude CLI format with result, sessionId, and metadata fields
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|ralph_loop.sh : Session management functions must auto-reset on: circuit breaker open, manual interrupt (SIGINT), project completion, or manual --reset-circuit flag, with history tracking in .ralph_session_history
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Implementation documentation in CLAUDE.md must remain synchronized with codebase - update when: new commands added to 'Key Commands', exit conditions change, installation instructions change, new Ralph loop behaviors introduced

Applied to files:

  • ralph_loop.sh
  • lib/response_analyzer.sh
  • tests/unit/test_exit_detection.bats
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to ralph_loop.sh : Ralph loop context injection via build_loop_context() must include: loop number, remaining tasks from fix_plan.md, circuit breaker state, and previous work summary

Applied to files:

  • ralph_loop.sh
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|ralph_loop.sh : Session management functions must auto-reset on: circuit breaker open, manual interrupt (SIGINT), project completion, or manual --reset-circuit flag, with history tracking in .ralph_session_history

Applied to files:

  • ralph_loop.sh
  • lib/response_analyzer.sh
  • tests/unit/test_exit_detection.bats
  • tests/integration/test_edge_cases.bats
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh : Use session management functions (store_session_id, get_last_session_id, should_resume_session) for maintaining context across Ralph loop iterations with 24-hour expiration

Applied to files:

  • ralph_loop.sh
  • lib/response_analyzer.sh
  • tests/unit/test_exit_detection.bats
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|**/ralph_import.sh : Implement JSON field extraction from Claude Code CLI responses supporting both flat format and Claude CLI format with result, sessionId, and metadata fields

Applied to files:

  • ralph_loop.sh
  • lib/response_analyzer.sh
  • tests/unit/test_exit_detection.bats
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh : Use JSON output format detection and parsing in Ralph scripts - implement `detect_output_format()` and `parse_json_response()` functions for Claude CLI integration

Applied to files:

  • ralph_loop.sh
  • lib/response_analyzer.sh
  • tests/unit/test_exit_detection.bats
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to ralph_loop.sh|ralph_monitor.sh|setup.sh|ralph_import.sh|install.sh : All Ralph CLI flags must support both long and short forms with comprehensive error handling for invalid inputs and boundary value validation

Applied to files:

  • ralph_loop.sh
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to tests/**/*.bats : Test files must follow naming convention: test_*.bats in tests/unit/ and tests/integration/ directories, organized by component (cli_parsing, json_parsing, session_continuity, exit_detection, etc.)

Applied to files:

  • tests/integration/test_edge_cases.bats
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: All new features must achieve 100% test pass rate before completion - test types required: unit tests for functions, integration tests for Ralph loop behavior, end-to-end tests for development cycles

Applied to files:

  • tests/integration/test_edge_cases.bats
🧬 Code graph analysis (2)
tests/unit/test_exit_detection.bats (2)
ralph_loop.sh (1)
  • should_exit_gracefully (274-351)
tests/helpers/test_helper.bash (1)
  • assert_equal (21-26)
tests/integration/test_edge_cases.bats (2)
lib/response_analyzer.sh (2)
  • analyze_response (198-462)
  • update_exit_signals (465-513)
tests/helpers/test_helper.bash (2)
  • assert_equal (21-26)
  • assert_file_exists (174-177)
🪛 Shellcheck (0.11.0)
lib/response_analyzer.sh

[warning] 307-307: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 308-308: Declare and assign separately to avoid masking return values.

(SC2155)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: coverage
  • GitHub Check: claude-review
🔇 Additional comments (15)
lib/response_analyzer.sh (2)

300-326: Explicit EXIT_SIGNAL handling looks correct.

The logic properly:

  1. Tracks whether an explicit EXIT_SIGNAL was found in the RALPH_STATUS block
  2. Respects EXIT_SIGNAL: false by not overriding with heuristics
  3. Falls back to STATUS-only completion when no explicit signal is provided

This correctly fixes the premature-exit bug described in the PR.


417-424: Heuristic gating correctly implemented.

The heuristic-based exit signal determination now only applies when no explicit EXIT_SIGNAL was found in the RALPH_STATUS block. This ensures Claude's explicit intent takes precedence over natural language pattern matching.

ralph_loop.sh (1)

312-327: Exit logic correctly implements the dual-condition check.

The implementation:

  1. Reads exit_signal from .response_analysis with proper fallback to "false"
  2. Requires BOTH completion_indicators >= 2 AND EXIT_SIGNAL == true to exit with project_complete
  3. Logs debug info when indicators are present but EXIT_SIGNAL is false

This correctly implements the decision matrix from the PR objectives.

tests/integration/test_edge_cases.bats (4)

422-456: Test correctly validates EXIT_SIGNAL=false is respected across iterations.

Good coverage of the scenario where Claude explicitly signals to continue despite completion-like language patterns.


458-500: Test correctly validates transition from IN_PROGRESS to COMPLETE.

Good coverage of the state transition scenario where Claude changes from EXIT_SIGNAL: false to EXIT_SIGNAL: true.


502-535: Test correctly validates graceful recovery from .response_analysis deletion.

Good resilience testing for the scenario where the analysis file gets deleted between iterations.


537-580: Test correctly validates JSON format response handling for EXIT_SIGNAL.

Good coverage of the JSON parsing path, validating that completion_status: "in_progress" and completion_status: "complete" correctly map to exit_signal: false and exit_signal: true respectively.

tests/unit/test_exit_detection.bats (8)

62-73: Helper function correctly mirrors production exit logic.

The updated should_exit_gracefully helper accurately reflects the dual-condition check from ralph_loop.sh, ensuring unit tests validate the actual production behavior.


150-167: Test correctly updated to require EXIT_SIGNAL for completion indicators exit.

The test now properly sets up the .response_analysis file with exit_signal: true, reflecting the new dual-condition requirement.


296-324: Test 21 correctly validates EXIT_SIGNAL=false prevents premature exit.

Good coverage of the core fix: high completion indicators should not trigger exit when Claude explicitly sets EXIT_SIGNAL: false.


326-354: Test 22 correctly validates the full exit condition.

Properly tests that both conditions (completion indicators >= 2 AND EXIT_SIGNAL=true) must be met for project_complete exit.


356-380: Tests 23-24 correctly validate fallback behavior for missing/invalid analysis files.

Good defensive testing ensuring the system defaults to continuing when .response_analysis is missing or corrupted.


382-401: Test 25 correctly validates threshold requirement.

Ensures that even with EXIT_SIGNAL=true, the completion indicators threshold must still be met.


441-474: Tests 28-29 correctly validate exit condition priority.

Good coverage ensuring that test_saturation and done_signals still take priority over the completion indicators check, regardless of EXIT_SIGNAL value.


476-489: Test 30 correctly handles empty analysis object.

Good edge case coverage for when the analysis object exists but is empty, ensuring safe default behavior.

Comment thread ralph_loop.sh
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@claude
Copy link
Copy Markdown

claude Bot commented Jan 13, 2026

Updated Code Review - EXIT_SIGNAL Respect Fix

Issue Number 1 - RESOLVED

Great work! The critical logic inconsistency I identified in my previous review has been fixed in commit 3fcaff8. The OR condition with STATUS has been removed, and now EXIT_SIGNAL takes proper precedence.

Remaining Items

Issue Number 2: Missing Test - I do not see a test that explicitly validates STATUS: COMPLETE but EXIT_SIGNAL: false. This edge case should be covered.

Issue Number 3: Error Handling - The jq parsing at ralph_loop.sh:318 could benefit from logging when parsing fails.

Documentation Update - CLAUDE.md Intelligent Exit Detection section should document that completion indicators now require EXIT_SIGNAL=true.

Overall Assessment

Status: Approve with minor recommendations

The critical Issue Number 1 has been fixed. Implementation is solid with 306 tests passing.

Remaining work (non-blocking): Add test for STATUS/EXIT_SIGNAL conflict, consider error logging, update CLAUDE.md.

The core fix is sound and ready to merge!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
lib/response_analyzer.sh (2)

307-308: Declare and assign separately to avoid masking return values.

Shellcheck SC2155: combining local with command substitution masks the return value. While acceptable here since empty values are valid fallbacks, separating declaration from assignment is cleaner.

♻️ Optional fix
     if grep -q -- "---RALPH_STATUS---" "$output_file"; then
         # Parse structured output
-        local status=$(grep "STATUS:" "$output_file" | cut -d: -f2 | xargs)
-        local exit_sig=$(grep "EXIT_SIGNAL:" "$output_file" | cut -d: -f2 | xargs)
+        local status
+        local exit_sig
+        status=$(grep "STATUS:" "$output_file" | cut -d: -f2 | xargs)
+        exit_sig=$(grep "EXIT_SIGNAL:" "$output_file" | cut -d: -f2 | xargs)

311-320: Consider case-insensitive comparison for EXIT_SIGNAL value.

The comparison "$exit_sig" == "true" is case-sensitive. If Claude outputs "TRUE" or "True", it would be treated as explicit false (falling to the else branch on line 317), which may not be the intended behavior.

♻️ Optional fix for case-insensitive handling
         if [[ -n "$exit_sig" ]]; then
             explicit_exit_signal_found=true
-            if [[ "$exit_sig" == "true" ]]; then
+            if [[ "${exit_sig,,}" == "true" ]]; then
                 has_completion_signal=true
                 exit_signal=true
                 confidence_score=100

Note: ${exit_sig,,} converts to lowercase (Bash 4+). Alternatively, use [[ "$exit_sig" =~ ^[Tt][Rr][Uu][Ee]$ ]].

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb201ae and 3fcaff8.

📒 Files selected for processing (1)
  • lib/response_analyzer.sh
🧰 Additional context used
📓 Path-based instructions (2)
**/response_analyzer.sh

📄 CodeRabbit inference engine (CLAUDE.md)

**/response_analyzer.sh: Use JSON output format detection and parsing in Ralph scripts - implement detect_output_format() and parse_json_response() functions for Claude CLI integration
Use session management functions (store_session_id, get_last_session_id, should_resume_session) for maintaining context across Ralph loop iterations with 24-hour expiration

Files:

  • lib/response_analyzer.sh
**/*.sh

📄 CodeRabbit inference engine (CLAUDE.md)

All bash scripts in Ralph must use array-based command building for CLI arguments to maintain shell injection safety

Files:

  • lib/response_analyzer.sh
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Implementation documentation in CLAUDE.md must remain synchronized with codebase - update when: new commands added to 'Key Commands', exit conditions change, installation instructions change, new Ralph loop behaviors introduced
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to ralph_loop.sh : Ralph loop context injection via build_loop_context() must include: loop number, remaining tasks from fix_plan.md, circuit breaker state, and previous work summary
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: All new features must achieve 100% test pass rate before completion - test types required: unit tests for functions, integration tests for Ralph loop behavior, end-to-end tests for development cycles
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh : Use JSON output format detection and parsing in Ralph scripts - implement `detect_output_format()` and `parse_json_response()` functions for Claude CLI integration

Applied to files:

  • lib/response_analyzer.sh
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|ralph_loop.sh : Session management functions must auto-reset on: circuit breaker open, manual interrupt (SIGINT), project completion, or manual --reset-circuit flag, with history tracking in .ralph_session_history

Applied to files:

  • lib/response_analyzer.sh
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|**/ralph_import.sh : Implement JSON field extraction from Claude Code CLI responses supporting both flat format and Claude CLI format with result, sessionId, and metadata fields

Applied to files:

  • lib/response_analyzer.sh
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh : Use session management functions (store_session_id, get_last_session_id, should_resume_session) for maintaining context across Ralph loop iterations with 24-hour expiration

Applied to files:

  • lib/response_analyzer.sh
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Implementation documentation in CLAUDE.md must remain synchronized with codebase - update when: new commands added to 'Key Commands', exit conditions change, installation instructions change, new Ralph loop behaviors introduced

Applied to files:

  • lib/response_analyzer.sh
🪛 Shellcheck (0.11.0)
lib/response_analyzer.sh

[warning] 307-307: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 308-308: Declare and assign separately to avoid masking return values.

(SC2155)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: coverage
  • GitHub Check: Macroscope - Correctness Check
  • GitHub Check: claude-review
🔇 Additional comments (3)
lib/response_analyzer.sh (3)

300-302: LGTM!

Clear variable naming and appropriate initialization. The comments effectively document the intent to prevent heuristic overrides when Claude provides an explicit signal.


417-424: LGTM!

The heuristic gating logic correctly implements the PR objective. Explicit EXIT_SIGNAL from Claude takes precedence over confidence-based heuristics, preventing premature exits when Claude explicitly signals to continue.


304-326: Confirmed: Missing EXIT_SIGNAL defaults correctly with test coverage.

When ---RALPH_STATUS--- is present but EXIT_SIGNAL: line is missing, the grep returns empty and the condition at line 312 correctly fails. The exit_signal variable (initialized to false at line 209) remains unchanged, allowing heuristics to apply while defaulting to "Continue" as specified in the PR decision matrix. Test 27 in test_exit_detection.bats explicitly validates this scenario: "should_exit_gracefully defaults to false when exit_signal field missing".

docs(exit): update CLAUDE.md with EXIT_SIGNAL gate documentation

- Added test for STATUS=COMPLETE with EXIT_SIGNAL=false conflict
  (EXIT_SIGNAL takes precedence, allowing phase completion without loop exit)
- Updated "Intelligent Exit Detection" section with dual-condition explanation
- Added "Completion Indicators with EXIT_SIGNAL Gate" section with decision table
- Documented conflict resolution behavior and implementation details
@claude
Copy link
Copy Markdown

claude Bot commented Jan 13, 2026

Code Review - EXIT_SIGNAL Respect Fix (Updated)

This PR successfully addresses the critical premature exit bug in Ralph's loop detection. After reviewing all three commits including the latest fixes, the implementation is now solid and ready to merge.


Key Improvements Verified

  1. Critical Logic Fix (commit 3fcaff8): The OR condition issue I identified in my first review has been properly resolved. EXIT_SIGNAL now correctly takes precedence over STATUS.

  2. Complete Test Coverage (commit 2b3fc83): Added the missing test for STATUS=COMPLETE with EXIT_SIGNAL=false conflict (Test 31 in test_exit_detection.bats). This validates that EXIT_SIGNAL takes precedence.

  3. Documentation Updated: CLAUDE.md now includes comprehensive documentation of the dual-condition exit logic, decision matrix, implementation details, conflict resolution behavior, and example scenarios.


Test Coverage

306 tests total with 100% pass rate

  • 10 new unit tests (tests 21-31 in test_exit_detection.bats)
  • 5 new integration tests (tests 21-25 in test_edge_cases.bats)

Edge cases covered:

  • Completion indicators + EXIT_SIGNAL=false continues
  • Completion indicators + EXIT_SIGNAL=true exits
  • Missing .response_analysis continues (safe default)
  • Malformed JSON continues (safe fallback)
  • STATUS=COMPLETE + EXIT_SIGNAL=false continues (precedence test)

Code Quality Assessment

ralph_loop.sh:312-327 - Clean dual-condition AND logic with safe default fallback and proper error handling

lib/response_analyzer.sh:310-326 - Proper precedence handling where EXIT_SIGNAL is checked first, explicit signal tracking prevents heuristic override


Security and Performance

  • Security: No concerns - purely control flow logic changes
  • Performance: Negligible impact (one jq call per loop iteration)
  • Reliability: Safe defaults prevent unintended exits

Minor Observations (Non-blocking)

  1. ShellCheck Warning (SC2155) in response_analyzer.sh:307-308 - Combining local with command substitution masks exit status. Current implementation works correctly but could be more explicit.

  2. ralph_loop.sh:318 could log when jq parsing fails for debugging. Current silent fallback is acceptable for production.


Overall Assessment

Status: APPROVED - Ready to Merge

This PR demonstrates clear problem identification, comprehensive test coverage, proper documentation, backward compatibility, and clean implementation following Ralph's conventions.

The fix correctly prevents premature exits when completion heuristics trigger during productive work phases, while still respecting Claude's explicit EXIT_SIGNAL when the project is truly complete.

Recommendation: Merge this PR. It significantly improves Ralph's reliability during autonomous development loops.

Excellent work on this critical bug fix!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CLAUDE.md (1)

9-9: Test counts are outdated.

The documentation states 276 tests passing and 20 tests in test_exit_detection.bats, but the PR adds 11 new tests (21-31) to that file and the PR objectives confirm 306 tests now pass. These counts should be updated to maintain synchronization between documentation and codebase.

Based on learnings, implementation documentation in CLAUDE.md must remain synchronized with codebase.

📝 Proposed fixes

Line 9:

-**Version**: v0.9.8 | **Tests**: 276 passing (100% pass rate) | **CI/CD**: GitHub Actions
+**Version**: v0.9.8 | **Tests**: 306 passing (100% pass rate) | **CI/CD**: GitHub Actions

Line 344:

-| `test_exit_detection.bats` | 20 | Exit signal detection |
+| `test_exit_detection.bats` | 30 | Exit signal detection + EXIT_SIGNAL gate |

Also applies to: 344-344

🧹 Nitpick comments (3)
CLAUDE.md (1)

182-192: Add language specifier to fenced code block.

The static analysis tool correctly identifies that this code block lacks a language specifier. Since this is an example output/behavior rather than executable code, use text or plaintext.

📝 Proposed fix
 **Example behavior when EXIT_SIGNAL is false:**
-```
+```text
 Loop 5: Claude outputs "Phase complete, moving to next feature"
tests/unit/test_exit_detection.bats (2)

302-303: Unnecessary mkdir -p for current directory.

Since RESPONSE_ANALYSIS_FILE=".response_analysis", dirname "$RESPONSE_ANALYSIS_FILE" evaluates to ., making mkdir -p "." a no-op. These lines can be removed for clarity, though they're harmless.

📝 Proposed cleanup (applied to both tests 21 and 22)
     # Setup: Claude's explicit exit signal is false (still working)
-    mkdir -p "$(dirname "$RESPONSE_ANALYSIS_FILE")"
     cat > "$RESPONSE_ANALYSIS_FILE" << 'EOF'

Also applies to: 332-333


29-73: Test helper duplicates production logic with untracked drift—add explicit sync requirement.

The should_exit_gracefully helper correctly mirrors the core exit signal gate logic from ralph_loop.sh, but code duplication creates maintenance risk. Production uses hardcoded .response_analysis (line 317 in ralph_loop.sh) while the test helper uses the variable $RESPONSE_ANALYSIS_FILE (line 56 in test_exit_detection.bats)—this inconsistency could cause silent bugs if either implementation drifts.

Sourcing the function directly is impractical due to production's log_status() dependency. Instead, add this comment to the test helper to establish an explicit contract:

# NOTE: This helper mirrors should_exit_gracefully() from ralph_loop.sh (lines 274-351).
# Critical sync points: file path references ($RESPONSE_ANALYSIS_FILE vs hardcoded path),
# exit condition order (test_saturation → completion_signals → project_complete → plan_complete),
# and condition thresholds (MAX_CONSECUTIVE_TEST_LOOPS, MAX_CONSECUTIVE_DONE_SIGNALS).
# If the implementation changes, update this helper accordingly.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fcaff8 and 2b3fc83.

📒 Files selected for processing (3)
  • CLAUDE.md
  • tests/integration/test_edge_cases.bats
  • tests/unit/test_exit_detection.bats
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/integration/test_edge_cases.bats
🧰 Additional context used
📓 Path-based instructions (1)
tests/**/*.bats

📄 CodeRabbit inference engine (CLAUDE.md)

Test files must follow naming convention: test_*.bats in tests/unit/ and tests/integration/ directories, organized by component (cli_parsing, json_parsing, session_continuity, exit_detection, etc.)

Files:

  • tests/unit/test_exit_detection.bats
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Implementation documentation in CLAUDE.md must remain synchronized with codebase - update when: new commands added to 'Key Commands', exit conditions change, installation instructions change, new Ralph loop behaviors introduced
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to ralph_loop.sh : Ralph loop context injection via build_loop_context() must include: loop number, remaining tasks from fix_plan.md, circuit breaker state, and previous work summary
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|ralph_loop.sh : Session management functions must auto-reset on: circuit breaker open, manual interrupt (SIGINT), project completion, or manual --reset-circuit flag, with history tracking in .ralph_session_history
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Implementation documentation in CLAUDE.md must remain synchronized with codebase - update when: new commands added to 'Key Commands', exit conditions change, installation instructions change, new Ralph loop behaviors introduced

Applied to files:

  • CLAUDE.md
  • tests/unit/test_exit_detection.bats
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|**/ralph_import.sh : Implement JSON field extraction from Claude Code CLI responses supporting both flat format and Claude CLI format with result, sessionId, and metadata fields

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to ralph_loop.sh : Ralph loop context injection via build_loop_context() must include: loop number, remaining tasks from fix_plan.md, circuit breaker state, and previous work summary

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh|ralph_loop.sh : Session management functions must auto-reset on: circuit breaker open, manual interrupt (SIGINT), project completion, or manual --reset-circuit flag, with history tracking in .ralph_session_history

Applied to files:

  • CLAUDE.md
  • tests/unit/test_exit_detection.bats
📚 Learning: 2025-12-31T19:31:02.350Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: templates/AGENT.md:0-0
Timestamp: 2025-12-31T19:31:02.350Z
Learning: Ensure CI/CD pipeline passes before considering feature complete

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: All new features must achieve 100% test pass rate before completion - test types required: unit tests for functions, integration tests for Ralph loop behavior, end-to-end tests for development cycles

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/circuit_breaker.sh : Circuit breaker implementation must support three states: CLOSED (normal operation), HALF_OPEN (monitoring mode), OPEN (halted) with configurable thresholds for no-progress and error detection

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/circuit_breaker.sh : Implement two-stage error filtering in circuit breaker detection - Stage 1 filters JSON field patterns, Stage 2 detects actual error contexts (Error:, Exception, Fatal, etc.)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to tests/**/*.bats : Test files must follow naming convention: test_*.bats in tests/unit/ and tests/integration/ directories, organized by component (cli_parsing, json_parsing, session_continuity, exit_detection, etc.)

Applied to files:

  • tests/unit/test_exit_detection.bats
📚 Learning: 2026-01-10T20:20:22.133Z
Learnt from: CR
Repo: frankbria/ralph-claude-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-10T20:20:22.133Z
Learning: Applies to **/response_analyzer.sh : Use session management functions (store_session_id, get_last_session_id, should_resume_session) for maintaining context across Ralph loop iterations with 24-hour expiration

Applied to files:

  • tests/unit/test_exit_detection.bats
🧬 Code graph analysis (1)
tests/unit/test_exit_detection.bats (2)
ralph_loop.sh (1)
  • should_exit_gracefully (274-351)
tests/helpers/test_helper.bash (1)
  • assert_equal (21-26)
🪛 markdownlint-cli2 (0.18.1)
CLAUDE.md

182-182: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: coverage
  • GitHub Check: claude-review
🔇 Additional comments (9)
CLAUDE.md (2)

168-194: EXIT_SIGNAL gate documentation is well-structured and accurate.

The dual-condition exit logic is clearly explained with a concrete example showing the difference between heuristic completion indicators and explicit EXIT_SIGNAL. The rationale section effectively communicates why this prevents false positives during productive work.


281-306: Decision matrix and implementation snippet are accurate.

The decision matrix clearly covers all combinations of completion_indicators, EXIT_SIGNAL, and .response_analysis states. The implementation snippet matches the actual code in ralph_loop.sh (lines 312-327 as documented). The conflict resolution note correctly explains EXIT_SIGNAL precedence over STATUS: COMPLETE.

tests/unit/test_exit_detection.bats (7)

6-22: Setup properly initializes the new RESPONSE_ANALYSIS_FILE variable.

The setup function correctly exports RESPONSE_ANALYSIS_FILE=".response_analysis" alongside the existing EXIT_SIGNALS_FILE, ensuring consistent test isolation across all EXIT_SIGNAL-related tests.


150-167: Test 8 correctly updated to require EXIT_SIGNAL=true.

The test now sets up both the completion indicators in .exit_signals AND exit_signal: true in .response_analysis, accurately reflecting the new dual-condition requirement for project_complete exit.


289-324: Test 21 provides excellent coverage for the core bug fix.

This test directly validates the fix—high completion indicators (3) with EXIT_SIGNAL: false correctly continues rather than exiting. The realistic .response_analysis structure with all relevant fields makes the test maintainable.


356-380: Tests 23-24 properly validate default-to-false behavior for edge cases.

Test 23 (missing file) and Test 24 (malformed JSON) correctly verify that the exit signal defaults to false when .response_analysis is unavailable or corrupt, ensuring graceful degradation without crashes.


382-401: Test 25 validates that EXIT_SIGNAL alone is insufficient.

This test ensures the dual-condition gate works in both directions—even with EXIT_SIGNAL: true, the completion indicators threshold (≥2) must also be met. This prevents premature exits when Claude signals completion before sufficient heuristic confirmation.


441-474: Tests 28-29 confirm priority order is preserved.

These tests verify that test_saturation and done_signals conditions are evaluated before completion indicators, matching the documented priority order. This ensures the EXIT_SIGNAL gate doesn't inadvertently affect other exit paths.


491-519: Test 31 covers the critical conflict resolution case.

This test validates the documented behavior: when STATUS: COMPLETE (high confidence_score, has_completion_signal) conflicts with EXIT_SIGNAL: false, the explicit EXIT_SIGNAL takes precedence. The work_summary "Phase complete, but more phases remain" clearly illustrates the real-world scenario this addresses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: completion_indicators exit check ignores Claude's explicit EXIT_SIGNAL: false

1 participant