From aa753c91588b4179cd704666bc3fa9b4d3df8886 Mon Sep 17 00:00:00 2001 From: Frank Bria Date: Mon, 2 Feb 2026 11:30:17 -0700 Subject: [PATCH] fix: progress detection improvements (#141, #144) (#158) * fix: progress detection improvements (#141, #144) - Fix checkbox regex to exclude date entries like [2026-01-29] (#144) - Add git commit detection: files changed in commits now count as progress (#141) - Add 13 regression tests for progress detection and checkbox regex - Update test count from 452 to 465 Fixes #141, Fixes #144 * fix(test): increase grep context to capture echo -1 line * fix: count both committed and working tree changes as progress When commits are made, now unions: - Files changed in commits (loop_start_sha..current_sha) - Unstaged changes (git diff HEAD) - Staged changes (git diff --cached) Uses sort -u to deduplicate before counting. --------- Co-authored-by: Test User --- CLAUDE.md | 2 +- README.md | 13 +- create_files.sh | 10 +- lib/response_analyzer.sh | 61 +++- ralph_loop.sh | 57 +++- tests/unit/test_exit_detection.bats | 357 +++++++++++++++++++++++- tests/unit/test_session_continuity.bats | 2 +- 7 files changed, 474 insertions(+), 28 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d681826..5f73a4e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -432,7 +432,7 @@ Ralph uses advanced error detection with two-stage filtering to eliminate false | `test_cli_modern.bats` | 29 | Modern CLI commands (Phase 1.1) + build_claude_command fix | | `test_json_parsing.bats` | 45 | JSON output format parsing + Claude CLI format + session management + array format | | `test_session_continuity.bats` | 28 | Session lifecycle management + circuit breaker integration + issue #91 fix | -| `test_exit_detection.bats` | 35 | Exit signal detection + EXIT_SIGNAL-based completion indicators | +| `test_exit_detection.bats` | 53 | Exit signal detection + EXIT_SIGNAL-based completion indicators + progress detection | | `test_rate_limiting.bats` | 15 | Rate limiting behavior | | `test_loop_execution.bats` | 20 | Integration tests | | `test_edge_cases.bats` | 20 | Edge case handling | diff --git a/README.md b/README.md index b3bd2b2..4f30a24 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![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.11.4-blue) -![Tests](https://img.shields.io/badge/tests-452%20passing-green) +![Tests](https://img.shields.io/badge/tests-465%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) @@ -18,7 +18,7 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t **Version**: v0.11.4 - Active Development **Core Features**: Working and tested -**Test Coverage**: 452 tests, 100% pass rate +**Test Coverage**: 465 tests, 100% pass rate ### What's Working Now - Autonomous development loops with intelligent exit detection @@ -43,6 +43,8 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t ### Recent Improvements **v0.11.4 - Bug Fixes & Compatibility** (latest) +- Fixed progress detection: Git commits within a loop now count as progress (#141) +- Fixed checkbox regex: Date entries `[2026-01-29]` no longer counted as checkboxes (#144) - Fixed session hijacking: Use `--resume ` instead of `--continue` (#151) - Fixed EXIT_SIGNAL override: `STATUS: COMPLETE` with `EXIT_SIGNAL: false` now continues working (#146) - Fixed ralph-import hanging indefinitely (added `--print` flag for non-interactive mode) @@ -50,6 +52,7 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t - Fixed cross-platform date commands for macOS with Homebrew coreutils - Added configurable circuit breaker thresholds via environment variables (#99) - Added tmux support for non-zero `base-index` configurations +- Added 13 new regression tests for progress detection and checkbox regex **v0.11.3 - Live Streaming & Beads Fix** - Added live streaming output mode with `--live` flag for real-time Claude Code visibility (#125) @@ -608,7 +611,7 @@ If you want to run the test suite: # Install BATS testing framework npm install -g bats bats-support bats-assert -# Run all tests (452 tests) +# Run all tests (465 tests) npm test # Run specific test suites @@ -633,7 +636,7 @@ bats tests/integration/test_installation.bats ``` Current test status: -- **452 tests** across 15 test files +- **465 tests** across 15 test files - **100% pass rate** (452/452 passing) - Comprehensive unit and integration tests - Specialized tests for JSON parsing, CLI flags, circuit breaker, EXIT_SIGNAL behavior, enable wizard, and installation workflows @@ -740,7 +743,7 @@ cd ralph-claude-code # Install dependencies and run tests npm install -npm test # All 452 tests must pass +npm test # All 465 tests must pass ``` ### Priority Contribution Areas diff --git a/create_files.sh b/create_files.sh index 25c3e95..d3bc99c 100755 --- a/create_files.sh +++ b/create_files.sh @@ -204,11 +204,13 @@ should_exit_gracefully() { fi # 4. Check fix_plan.md for completion - # Bug #3 Fix: Support indented markdown checkboxes with [[:space:]]* pattern + # Fix #144: Only match valid markdown checkboxes, not date entries like [2026-01-29] + # Valid patterns: "- [ ]" (uncompleted) and "- [x]" or "- [X]" (completed) if [[ -f "$RALPH_DIR/fix_plan.md" ]]; then - 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") - + local uncompleted_items=$(grep -cE "^[[:space:]]*- \[ \]" "$RALPH_DIR/fix_plan.md" 2>/dev/null || echo "0") + local completed_items=$(grep -cE "^[[:space:]]*- \[[xX]\]" "$RALPH_DIR/fix_plan.md" 2>/dev/null || echo "0") + local total_items=$((uncompleted_items + completed_items)) + 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)" echo "plan_complete" diff --git a/lib/response_analyzer.sh b/lib/response_analyzer.sh index 7ba30c1..d127880 100644 --- a/lib/response_analyzer.sh +++ b/lib/response_analyzer.sh @@ -358,8 +358,37 @@ analyze_response() { fi # Check for file changes via git (supplements JSON data) + # Fix #141: Detect both uncommitted changes AND committed changes if command -v git &>/dev/null && git rev-parse --git-dir >/dev/null 2>&1; then - local git_files=$(git diff --name-only 2>/dev/null | wc -l) + local git_files=0 + local loop_start_sha="" + local current_sha="" + + if [[ -f "$RALPH_DIR/.loop_start_sha" ]]; then + loop_start_sha=$(cat "$RALPH_DIR/.loop_start_sha" 2>/dev/null || echo "") + fi + current_sha=$(git rev-parse HEAD 2>/dev/null || echo "") + + # Check if commits were made (HEAD changed) + if [[ -n "$loop_start_sha" && -n "$current_sha" && "$loop_start_sha" != "$current_sha" ]]; then + # Commits were made - count union of committed files AND working tree changes + git_files=$( + { + git diff --name-only "$loop_start_sha" "$current_sha" 2>/dev/null + git diff --name-only HEAD 2>/dev/null # unstaged changes + git diff --name-only --cached 2>/dev/null # staged changes + } | sort -u | wc -l + ) + else + # No commits - check for uncommitted changes (staged + unstaged) + git_files=$( + { + git diff --name-only 2>/dev/null # unstaged changes + git diff --name-only --cached 2>/dev/null # staged changes + } | sort -u | wc -l + ) + fi + if [[ $git_files -gt 0 ]]; then has_progress=true files_modified=$git_files @@ -500,8 +529,36 @@ analyze_response() { done # 6. Check for file changes (git integration) + # Fix #141: Detect both uncommitted changes AND committed changes if command -v git &>/dev/null && git rev-parse --git-dir >/dev/null 2>&1; then - files_modified=$(git diff --name-only 2>/dev/null | wc -l) + local loop_start_sha="" + local current_sha="" + + if [[ -f "$RALPH_DIR/.loop_start_sha" ]]; then + loop_start_sha=$(cat "$RALPH_DIR/.loop_start_sha" 2>/dev/null || echo "") + fi + current_sha=$(git rev-parse HEAD 2>/dev/null || echo "") + + # Check if commits were made (HEAD changed) + if [[ -n "$loop_start_sha" && -n "$current_sha" && "$loop_start_sha" != "$current_sha" ]]; then + # Commits were made - count union of committed files AND working tree changes + files_modified=$( + { + git diff --name-only "$loop_start_sha" "$current_sha" 2>/dev/null + git diff --name-only HEAD 2>/dev/null # unstaged changes + git diff --name-only --cached 2>/dev/null # staged changes + } | sort -u | wc -l + ) + else + # No commits - check for uncommitted changes (staged + unstaged) + files_modified=$( + { + git diff --name-only 2>/dev/null # unstaged changes + git diff --name-only --cached 2>/dev/null # staged changes + } | sort -u | wc -l + ) + fi + if [[ $files_modified -gt 0 ]]; then has_progress=true ((confidence_score+=20)) diff --git a/ralph_loop.sh b/ralph_loop.sh index 896a554..f932a78 100755 --- a/ralph_loop.sh +++ b/ralph_loop.sh @@ -492,15 +492,12 @@ should_exit_gracefully() { fi # 5. Check fix_plan.md for completion - # Bug #3 Fix: Support indented markdown checkboxes with [[:space:]]* pattern + # Fix #144: Only match valid markdown checkboxes, not date entries like [2026-01-29] + # Valid patterns: "- [ ]" (uncompleted) and "- [x]" or "- [X]" (completed) if [[ -f "$RALPH_DIR/fix_plan.md" ]]; then - 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 - [[ -z "$completed_items" ]] && completed_items=0 - + local uncompleted_items=$(grep -cE "^[[:space:]]*- \[ \]" "$RALPH_DIR/fix_plan.md" 2>/dev/null || echo "0") + local completed_items=$(grep -cE "^[[:space:]]*- \[[xX]\]" "$RALPH_DIR/fix_plan.md" 2>/dev/null || echo "0") + local total_items=$((uncompleted_items + completed_items)) 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)" >&2 @@ -1013,6 +1010,14 @@ execute_claude_code() { local calls_made=$(cat "$CALL_COUNT_FILE" 2>/dev/null || echo "0") calls_made=$((calls_made + 1)) + # Fix #141: Capture git HEAD SHA at loop start to detect commits as progress + # Store in file for access by progress detection after Claude execution + local loop_start_sha="" + if command -v git &>/dev/null && git rev-parse --git-dir &>/dev/null 2>&1; then + loop_start_sha=$(git rev-parse HEAD 2>/dev/null || echo "") + fi + echo "$loop_start_sha" > "$RALPH_DIR/.loop_start_sha" + log_status "LOOP" "Executing Claude Code (Call $calls_made/$MAX_CALLS_PER_HOUR)" local timeout_seconds=$((CLAUDE_TIMEOUT_MINUTES * 60)) log_status "INFO" "⏳ Starting Claude Code execution... (timeout: ${CLAUDE_TIMEOUT_MINUTES}m)" @@ -1279,7 +1284,41 @@ EOF log_analysis_summary # Get file change count for circuit breaker - local files_changed=$(git diff --name-only 2>/dev/null | wc -l || echo 0) + # Fix #141: Detect both uncommitted changes AND committed changes + local files_changed=0 + local loop_start_sha="" + local current_sha="" + + if [[ -f "$RALPH_DIR/.loop_start_sha" ]]; then + loop_start_sha=$(cat "$RALPH_DIR/.loop_start_sha" 2>/dev/null || echo "") + fi + + if command -v git &>/dev/null && git rev-parse --git-dir &>/dev/null 2>&1; then + current_sha=$(git rev-parse HEAD 2>/dev/null || echo "") + + # Check if commits were made (HEAD changed) + if [[ -n "$loop_start_sha" && -n "$current_sha" && "$loop_start_sha" != "$current_sha" ]]; then + # Commits were made - count union of committed files AND working tree changes + # This catches cases where Claude commits some files but still has other modified files + files_changed=$( + { + git diff --name-only "$loop_start_sha" "$current_sha" 2>/dev/null + git diff --name-only HEAD 2>/dev/null # unstaged changes + git diff --name-only --cached 2>/dev/null # staged changes + } | sort -u | wc -l + ) + [[ "$VERBOSE_PROGRESS" == "true" ]] && log_status "DEBUG" "Detected $files_changed unique files changed (commits + working tree) since loop start" + else + # No commits - check for uncommitted changes (staged + unstaged) + files_changed=$( + { + git diff --name-only 2>/dev/null # unstaged changes + git diff --name-only --cached 2>/dev/null # staged changes + } | sort -u | wc -l + ) + fi + fi + local has_errors="false" # Two-stage error detection to avoid JSON field false positives diff --git a/tests/unit/test_exit_detection.bats b/tests/unit/test_exit_detection.bats index 3c2a6fa..b87e8e3 100644 --- a/tests/unit/test_exit_detection.bats +++ b/tests/unit/test_exit_detection.bats @@ -75,13 +75,15 @@ should_exit_gracefully() { fi # 4. Check fix_plan.md for completion + # Fix #144: Only match valid markdown checkboxes, not date entries like [2026-01-29] 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) - - # Handle case where grep returns no matches (exit code 1) - [[ -z "$total_items" ]] && total_items=0 - [[ -z "$completed_items" ]] && completed_items=0 + local uncompleted_items + local completed_items + uncompleted_items=$(grep -cE "^[[:space:]]*- \[ \]" "$RALPH_DIR/fix_plan.md" 2>/dev/null || echo "0") + uncompleted_items=$(echo "$uncompleted_items" | tr -d '[:space:]') + completed_items=$(grep -cE "^[[:space:]]*- \[[xX]\]" "$RALPH_DIR/fix_plan.md" 2>/dev/null || echo "0") + completed_items=$(echo "$completed_items" | tr -d '[:space:]') + local total_items=$((uncompleted_items + completed_items)) if [[ $total_items -gt 0 ]] && [[ $completed_items -eq $total_items ]]; then echo "plan_complete" @@ -878,3 +880,346 @@ EOF result=$(should_exit_gracefully_with_denials || true) assert_equal "$result" "" } + +# ============================================================================= +# CHECKBOX REGEX FIX TESTS (Issue #144) +# ============================================================================= +# These tests verify that date entries like [2026-01-29] are NOT counted as +# checkboxes, preventing false "plan_complete" exits when fix_plan.md contains +# dated entries that match the old [*] pattern. + +# Test 41: Date entries should NOT be counted as checkboxes +@test "fix_plan.md date entries are not counted as checkboxes" { + cat > "$RALPH_DIR/fix_plan.md" << 'EOF' +# Fix Plan + +## Changelog +- [2026-01-29] Initial version +- [2026-01-30] Added feature X +- [2026-01-31] Bug fixes + +## Tasks +- [ ] Task 1 pending +- [ ] Task 2 pending +EOF + + result=$(should_exit_gracefully || true) + # Should NOT exit - there are 2 uncompleted tasks + assert_equal "$result" "" +} + +# Test 42: Date entries mixed with completed tasks should not cause false exit +@test "fix_plan.md with dates and completed tasks counts correctly" { + cat > "$RALPH_DIR/fix_plan.md" << 'EOF' +# Fix Plan + +## Changelog +- [2026-01-29] Initial version + +## Tasks +- [x] Task 1 complete +- [ ] Task 2 pending +EOF + + result=$(should_exit_gracefully || true) + # 1 completed, 1 pending - should NOT exit + assert_equal "$result" "" +} + +# Test 43: Non-checkbox bracket patterns (NOTE, TODO, FIXME) should be excluded +@test "fix_plan.md bracket patterns like [NOTE] are not checkboxes" { + cat > "$RALPH_DIR/fix_plan.md" << 'EOF' +# Fix Plan + +## Notes +- [NOTE] Remember to update docs +- [TODO] Consider refactoring later +- [FIXME] Known issue with edge case +- [WIP] Work in progress + +## Tasks +- [ ] Task 1 pending +EOF + + result=$(should_exit_gracefully || true) + # Only 1 real task (pending) - should NOT exit + assert_equal "$result" "" +} + +# Test 44: Case-insensitive completed checkboxes ([x] and [X]) +@test "fix_plan.md counts both [x] and [X] as completed" { + cat > "$RALPH_DIR/fix_plan.md" << 'EOF' +# Fix Plan +- [x] Task 1 with lowercase x +- [X] Task 2 with uppercase X +- [x] Task 3 with lowercase x +EOF + + result=$(should_exit_gracefully) + # All 3 tasks completed - should exit with plan_complete + assert_equal "$result" "plan_complete" +} + +# Test 45: Indented date entries should not be counted +@test "fix_plan.md indented date entries are not checkboxes" { + cat > "$RALPH_DIR/fix_plan.md" << 'EOF' +# Fix Plan + +## Releases + - [2026-01-29] v1.0.0 released + - [2026-01-30] v1.0.1 patch + +## Tasks +- [x] All done +EOF + + result=$(should_exit_gracefully) + # Only 1 real task (completed) - should exit + assert_equal "$result" "plan_complete" +} + +# Test 46: Empty checkbox [ ] with spaces should be counted as uncompleted +@test "fix_plan.md empty checkboxes with extra spaces" { + cat > "$RALPH_DIR/fix_plan.md" << 'EOF' +# Fix Plan +- [ ] Task with single space (valid) +- [ ] Task with double space (invalid format, not counted) +- [x] Completed task +EOF + + result=$(should_exit_gracefully || true) + # 1 uncompleted, 1 completed - should NOT exit + assert_equal "$result" "" +} + +# Test 47: Version numbers in brackets should not be counted +@test "fix_plan.md version numbers like [v1.0] are not checkboxes" { + cat > "$RALPH_DIR/fix_plan.md" << 'EOF' +# Fix Plan + +## Version History +- [v1.0] Initial release +- [v1.1] Added features +- [v2.0] Major update + +## Tasks +- [x] Task complete +EOF + + result=$(should_exit_gracefully) + # Only 1 real task (completed) - should exit + assert_equal "$result" "plan_complete" +} + +# Test 48: Issue/PR references should not be counted +@test "fix_plan.md issue references like [#123] are not checkboxes" { + cat > "$RALPH_DIR/fix_plan.md" << 'EOF' +# Fix Plan + +## Related Issues +- [#141] Progress detection bug +- [#144] Checkbox regex false positives +- [PR#155] Setup improvements + +## Tasks +- [ ] Fix issue #141 +- [ ] Fix issue #144 +EOF + + result=$(should_exit_gracefully || true) + # 2 uncompleted tasks - should NOT exit + assert_equal "$result" "" +} + +# ============================================================================= +# GIT COMMIT DETECTION TESTS (Issue #141) +# ============================================================================= +# These tests verify that when Claude commits within a loop, the committed files +# are counted as progress even though there are no uncommitted changes. + +# Helper function to detect progress including git commits +detect_progress_with_commits() { + local loop_start_sha="$1" + local current_sha="$2" + local files_changed=0 + + # Check for committed changes since loop start + if [[ -n "$loop_start_sha" && -n "$current_sha" && "$loop_start_sha" != "$current_sha" ]]; then + # Files changed in commits between loop start and current HEAD + files_changed=$(git diff --name-only "$loop_start_sha" "$current_sha" 2>/dev/null | wc -l || echo 0) + files_changed=$(echo "$files_changed" | tr -d ' ') + else + # Fall back to uncommitted changes + files_changed=$(git diff --name-only 2>/dev/null | wc -l || echo 0) + files_changed=$(echo "$files_changed" | tr -d ' ') + fi + + echo "$files_changed" +} + +# Test 49: Git commit detection - files changed in commit count as progress +@test "git commit detection counts committed files as progress" { + # Skip if git is not available + if ! command -v git &>/dev/null; then + skip "git not available" + fi + + # Initialize a git repo + git init --quiet + git config user.email "test@test.com" + git config user.name "Test" + + # Create initial commit + echo "initial" > file1.txt + git add file1.txt + git commit --quiet -m "Initial commit" + + local loop_start_sha=$(git rev-parse HEAD) + + # Simulate Claude making changes and committing within the loop + echo "modified" > file1.txt + echo "new file" > file2.txt + git add file1.txt file2.txt + git commit --quiet -m "Claude's work" + + local current_sha=$(git rev-parse HEAD) + + # Detect progress + local files_changed=$(detect_progress_with_commits "$loop_start_sha" "$current_sha") + + # Should detect 2 files changed + [ "$files_changed" -eq 2 ] +} + +# Test 50: Git commit detection - no progress when SHA unchanged +@test "git commit detection returns 0 when no commits made" { + # Skip if git is not available + if ! command -v git &>/dev/null; then + skip "git not available" + fi + + # Initialize a git repo + git init --quiet + git config user.email "test@test.com" + git config user.name "Test" + + # Create initial commit + echo "initial" > file1.txt + git add file1.txt + git commit --quiet -m "Initial commit" + + local loop_start_sha=$(git rev-parse HEAD) + local current_sha=$(git rev-parse HEAD) + + # No uncommitted changes either + local files_changed=$(detect_progress_with_commits "$loop_start_sha" "$current_sha") + + # Should detect 0 files (no commits, no uncommitted changes) + [ "$files_changed" -eq 0 ] +} + +# Test 51: Git commit detection - falls back to uncommitted when no commit +@test "git commit detection falls back to uncommitted changes" { + # Skip if git is not available + if ! command -v git &>/dev/null; then + skip "git not available" + fi + + # Initialize a git repo + git init --quiet + git config user.email "test@test.com" + git config user.name "Test" + + # Create initial commit with two tracked files + echo "initial1" > file1.txt + echo "initial2" > file2.txt + git add file1.txt file2.txt + git commit --quiet -m "Initial commit" + + local loop_start_sha=$(git rev-parse HEAD) + + # Make uncommitted changes to tracked files (no commit) + echo "modified1" > file1.txt + echo "modified2" > file2.txt + + local current_sha=$(git rev-parse HEAD) # Same as loop_start_sha + + # Detect progress - should fall back to uncommitted changes + # Note: git diff only shows modified tracked files, not untracked files + local files_changed=$(detect_progress_with_commits "$loop_start_sha" "$current_sha") + + # Should detect 2 uncommitted modified files + [ "$files_changed" -eq 2 ] +} + +# Test 52: Git commit detection - multiple commits within loop +@test "git commit detection counts files across multiple commits" { + # Skip if git is not available + if ! command -v git &>/dev/null; then + skip "git not available" + fi + + # Initialize a git repo + git init --quiet + git config user.email "test@test.com" + git config user.name "Test" + + # Create initial commit + echo "initial" > file1.txt + git add file1.txt + git commit --quiet -m "Initial commit" + + local loop_start_sha=$(git rev-parse HEAD) + + # First commit within loop + echo "change1" > file1.txt + git add file1.txt + git commit --quiet -m "First change" + + # Second commit within loop + echo "new" > file2.txt + git add file2.txt + git commit --quiet -m "Second change" + + # Third commit within loop + echo "another" > file3.txt + git add file3.txt + git commit --quiet -m "Third change" + + local current_sha=$(git rev-parse HEAD) + + # Detect progress + local files_changed=$(detect_progress_with_commits "$loop_start_sha" "$current_sha") + + # Should detect 3 files (one per commit) + [ "$files_changed" -eq 3 ] +} + +# Test 53: Git commit detection handles empty loop_start_sha +@test "git commit detection handles missing loop_start_sha" { + # Skip if git is not available + if ! command -v git &>/dev/null; then + skip "git not available" + fi + + # Initialize a git repo + git init --quiet + git config user.email "test@test.com" + git config user.name "Test" + + # Create initial commit + echo "initial" > file1.txt + git add file1.txt + git commit --quiet -m "Initial commit" + + # Make uncommitted changes + echo "modified" > file1.txt + + local current_sha=$(git rev-parse HEAD) + + # Detect progress with empty loop_start_sha + local files_changed=$(detect_progress_with_commits "" "$current_sha") + + # Should fall back to uncommitted changes (1 file) + [ "$files_changed" -eq 1 ] +} diff --git a/tests/unit/test_session_continuity.bats b/tests/unit/test_session_continuity.bats index 4e0c710..6236ff8 100644 --- a/tests/unit/test_session_continuity.bats +++ b/tests/unit/test_session_continuity.bats @@ -412,7 +412,7 @@ EOF echo "test" > "$test_file" # Verify the function code handles stat failure by checking the implementation - run grep -A25 'get_session_file_age_hours' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" + run grep -A35 'get_session_file_age_hours' "${BATS_TEST_DIRNAME}/../../ralph_loop.sh" [[ "$output" == *'echo "-1"'* ]] }