fix(analyzer): address code review feedback
- Fix BSD date parsing to handle milliseconds in ISO timestamps (e.g., 2026-01-09T10:30:00.123+00:00) - Document error_count mapping behavior when only has_errors=true is present - Remove unused has_session_id_field variable - Add debug logging for session persistence (controlled by VERBOSE_PROGRESS) - Standardize session filename to .claude_session_id across all files All 239 tests passing.
This commit is contained in:
parent
fdff095c18
commit
a2e7e9385c
3 changed files with 32 additions and 20 deletions
|
|
@ -36,7 +36,7 @@ The system uses a modular architecture with reusable components in the `lib/` di
|
||||||
- Supports both flat JSON format and Claude CLI format (`result`, `sessionId`, `metadata`)
|
- Supports both flat JSON format and Claude CLI format (`result`, `sessionId`, `metadata`)
|
||||||
- Extracts structured fields: status, exit_signal, work_type, files_modified
|
- Extracts structured fields: status, exit_signal, work_type, files_modified
|
||||||
- **Session management**: `store_session_id()`, `get_last_session_id()`, `should_resume_session()`
|
- **Session management**: `store_session_id()`, `get_last_session_id()`, `should_resume_session()`
|
||||||
- Automatic session persistence to `.session_id` file with 24-hour expiration
|
- Automatic session persistence to `.claude_session_id` file with 24-hour expiration
|
||||||
- Detects test-only loops and stuck error patterns
|
- Detects test-only loops and stuck error patterns
|
||||||
- Two-stage error filtering to eliminate false positives
|
- Two-stage error filtering to eliminate false positives
|
||||||
- Multi-line error matching for accurate stuck loop detection
|
- Multi-line error matching for accurate stuck loop detection
|
||||||
|
|
@ -314,7 +314,7 @@ bats tests/unit/test_cli_parsing.bats
|
||||||
- `get_last_session_id()` - Retrieves stored session ID
|
- `get_last_session_id()` - Retrieves stored session ID
|
||||||
- `should_resume_session()` - Checks session validity (24-hour expiration)
|
- `should_resume_session()` - Checks session validity (24-hour expiration)
|
||||||
- Added `get_epoch_seconds()` to date_utils.sh for cross-platform epoch time
|
- Added `get_epoch_seconds()` to date_utils.sh for cross-platform epoch time
|
||||||
- Auto-persists sessionId to `.session_id` file during response analysis
|
- Auto-persists sessionId to `.claude_session_id` file during response analysis
|
||||||
- Added 16 new tests covering Claude CLI format and session management
|
- Added 16 new tests covering Claude CLI format and session management
|
||||||
- Test count: 239 (up from 223)
|
- Test count: 239 (up from 223)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,6 @@ parse_json_response() {
|
||||||
|
|
||||||
# Detect JSON format by checking for Claude CLI fields
|
# Detect JSON format by checking for Claude CLI fields
|
||||||
local has_result_field=$(jq -r 'has("result")' "$output_file" 2>/dev/null)
|
local has_result_field=$(jq -r 'has("result")' "$output_file" 2>/dev/null)
|
||||||
local has_session_id_field=$(jq -r 'has("sessionId")' "$output_file" 2>/dev/null)
|
|
||||||
|
|
||||||
# Extract fields - support both flat format and Claude CLI format
|
# Extract fields - support both flat format and Claude CLI format
|
||||||
# Priority: Claude CLI fields first, then flat format fields
|
# Priority: Claude CLI fields first, then flat format fields
|
||||||
|
|
@ -94,6 +93,10 @@ parse_json_response() {
|
||||||
local files_modified=$(jq -r '.metadata.files_changed // .files_modified // 0' "$output_file" 2>/dev/null)
|
local files_modified=$(jq -r '.metadata.files_changed // .files_modified // 0' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
# Error count: from flat format OR derived from metadata.has_errors
|
# Error count: from flat format OR derived from metadata.has_errors
|
||||||
|
# Note: When only has_errors=true is present (without explicit error_count),
|
||||||
|
# we set error_count=1 as a minimum. This is defensive programming since
|
||||||
|
# the stuck detection threshold is >5 errors, so 1 error won't trigger it.
|
||||||
|
# Actual error count may be higher, but precise count isn't critical for our logic.
|
||||||
local error_count=$(jq -r '.error_count // 0' "$output_file" 2>/dev/null)
|
local error_count=$(jq -r '.error_count // 0' "$output_file" 2>/dev/null)
|
||||||
local has_errors=$(jq -r '.metadata.has_errors // false' "$output_file" 2>/dev/null)
|
local has_errors=$(jq -r '.metadata.has_errors // false' "$output_file" 2>/dev/null)
|
||||||
if [[ "$has_errors" == "true" && "$error_count" == "0" ]]; then
|
if [[ "$has_errors" == "true" && "$error_count" == "0" ]]; then
|
||||||
|
|
@ -232,9 +235,10 @@ analyze_response() {
|
||||||
local json_confidence=$(jq -r '.confidence' .json_parse_result 2>/dev/null || echo "0")
|
local json_confidence=$(jq -r '.confidence' .json_parse_result 2>/dev/null || echo "0")
|
||||||
local session_id=$(jq -r '.session_id' .json_parse_result 2>/dev/null || echo "")
|
local session_id=$(jq -r '.session_id' .json_parse_result 2>/dev/null || echo "")
|
||||||
|
|
||||||
# Persist session ID if present
|
# Persist session ID if present (for session continuity across loop iterations)
|
||||||
if [[ -n "$session_id" && "$session_id" != "null" ]]; then
|
if [[ -n "$session_id" && "$session_id" != "null" ]]; then
|
||||||
store_session_id "$session_id"
|
store_session_id "$session_id"
|
||||||
|
[[ "${VERBOSE_PROGRESS:-}" == "true" ]] && echo "DEBUG: Persisted session ID: $session_id" >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# JSON parsing provides high confidence
|
# JSON parsing provides high confidence
|
||||||
|
|
@ -567,8 +571,8 @@ detect_stuck_loop() {
|
||||||
# SESSION MANAGEMENT FUNCTIONS
|
# SESSION MANAGEMENT FUNCTIONS
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# Session file location
|
# Session file location - standardized across ralph_loop.sh and response_analyzer.sh
|
||||||
SESSION_FILE=".session_id"
|
SESSION_FILE=".claude_session_id"
|
||||||
# Session expiration time in seconds (24 hours)
|
# Session expiration time in seconds (24 hours)
|
||||||
SESSION_EXPIRATION_SECONDS=86400
|
SESSION_EXPIRATION_SECONDS=86400
|
||||||
|
|
||||||
|
|
@ -628,16 +632,24 @@ should_resume_session() {
|
||||||
local session_time
|
local session_time
|
||||||
|
|
||||||
# Parse ISO timestamp to epoch - try multiple formats for cross-platform compatibility
|
# Parse ISO timestamp to epoch - try multiple formats for cross-platform compatibility
|
||||||
|
# Strip milliseconds if present (e.g., 2026-01-09T10:30:00.123+00:00 → 2026-01-09T10:30:00+00:00)
|
||||||
|
local clean_timestamp="${timestamp}"
|
||||||
|
if [[ "$timestamp" =~ \.[0-9]+[+-Z] ]]; then
|
||||||
|
clean_timestamp=$(echo "$timestamp" | sed 's/\.[0-9]*\([+-Z]\)/\1/')
|
||||||
|
fi
|
||||||
|
|
||||||
if command -v gdate &>/dev/null; then
|
if command -v gdate &>/dev/null; then
|
||||||
# macOS with coreutils
|
# macOS with coreutils
|
||||||
session_time=$(gdate -d "$timestamp" +%s 2>/dev/null)
|
session_time=$(gdate -d "$clean_timestamp" +%s 2>/dev/null)
|
||||||
elif date --version 2>&1 | grep -q GNU; then
|
elif date --version 2>&1 | grep -q GNU; then
|
||||||
# GNU date (Linux)
|
# GNU date (Linux)
|
||||||
session_time=$(date -d "$timestamp" +%s 2>/dev/null)
|
session_time=$(date -d "$clean_timestamp" +%s 2>/dev/null)
|
||||||
else
|
else
|
||||||
# BSD date (macOS without coreutils) - try parsing ISO format
|
# BSD date (macOS without coreutils) - try parsing ISO format
|
||||||
# Format: 2026-01-09T10:30:00+00:00 or similar
|
# Format: 2026-01-09T10:30:00+00:00 or 2026-01-09T10:30:00Z
|
||||||
session_time=$(date -j -f "%Y-%m-%dT%H:%M:%S" "${timestamp%[+-]*}" +%s 2>/dev/null)
|
# Strip timezone suffix for BSD date parsing
|
||||||
|
local date_only="${clean_timestamp%[+-Z]*}"
|
||||||
|
session_time=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$date_only" +%s 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If we couldn't parse the timestamp, consider session expired
|
# If we couldn't parse the timestamp, consider session expired
|
||||||
|
|
|
||||||
|
|
@ -657,7 +657,7 @@ EOF
|
||||||
assert_equal "$output_format" "json"
|
assert_equal "$output_format" "json"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "analyze_response persists sessionId to .session_id file" {
|
@test "analyze_response persists sessionId to .claude_session_id file" {
|
||||||
local output_file="$LOG_DIR/test_output.log"
|
local output_file="$LOG_DIR/test_output.log"
|
||||||
|
|
||||||
cat > "$output_file" << 'EOF'
|
cat > "$output_file" << 'EOF'
|
||||||
|
|
@ -670,9 +670,9 @@ EOF
|
||||||
analyze_response "$output_file" 1
|
analyze_response "$output_file" 1
|
||||||
|
|
||||||
# Session ID should be persisted for continuity
|
# Session ID should be persisted for continuity
|
||||||
[[ -f ".session_id" ]] || skip "Session persistence not yet implemented"
|
[[ -f ".claude_session_id" ]] || skip "Session persistence not yet implemented"
|
||||||
|
|
||||||
local stored_session=$(cat .session_id)
|
local stored_session=$(cat .claude_session_id)
|
||||||
[[ "$stored_session" == *"session-persist-test-123"* ]]
|
[[ "$stored_session" == *"session-persist-test-123"* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -683,15 +683,15 @@ EOF
|
||||||
@test "store_session_id writes session to file with timestamp" {
|
@test "store_session_id writes session to file with timestamp" {
|
||||||
run store_session_id "session-test-abc"
|
run store_session_id "session-test-abc"
|
||||||
|
|
||||||
[[ -f ".session_id" ]] || skip "store_session_id not yet implemented"
|
[[ -f ".claude_session_id" ]] || skip "store_session_id not yet implemented"
|
||||||
|
|
||||||
local content=$(cat .session_id)
|
local content=$(cat .claude_session_id)
|
||||||
[[ "$content" == *"session-test-abc"* ]]
|
[[ "$content" == *"session-test-abc"* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "get_last_session_id retrieves stored session" {
|
@test "get_last_session_id retrieves stored session" {
|
||||||
# First store a session
|
# First store a session
|
||||||
echo '{"session_id": "session-retrieve-test", "timestamp": "2026-01-09T10:00:00Z"}' > .session_id
|
echo '{"session_id": "session-retrieve-test", "timestamp": "2026-01-09T10:00:00Z"}' > .claude_session_id
|
||||||
|
|
||||||
run get_last_session_id
|
run get_last_session_id
|
||||||
|
|
||||||
|
|
@ -699,7 +699,7 @@ EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "get_last_session_id returns empty when no session file" {
|
@test "get_last_session_id returns empty when no session file" {
|
||||||
rm -f .session_id
|
rm -f .claude_session_id
|
||||||
|
|
||||||
run get_last_session_id
|
run get_last_session_id
|
||||||
|
|
||||||
|
|
@ -711,7 +711,7 @@ EOF
|
||||||
@test "should_resume_session returns true for recent session" {
|
@test "should_resume_session returns true for recent session" {
|
||||||
# Store a recent session (simulated as current timestamp)
|
# Store a recent session (simulated as current timestamp)
|
||||||
local now=$(date +%s)
|
local now=$(date +%s)
|
||||||
echo "{\"session_id\": \"session-recent\", \"timestamp\": \"$(date -Iseconds)\"}" > .session_id
|
echo "{\"session_id\": \"session-recent\", \"timestamp\": \"$(date -Iseconds)\"}" > .claude_session_id
|
||||||
|
|
||||||
run should_resume_session
|
run should_resume_session
|
||||||
|
|
||||||
|
|
@ -721,7 +721,7 @@ EOF
|
||||||
|
|
||||||
@test "should_resume_session returns false for old session" {
|
@test "should_resume_session returns false for old session" {
|
||||||
# Store an old session (24+ hours ago)
|
# Store an old session (24+ hours ago)
|
||||||
echo '{"session_id": "session-old", "timestamp": "2020-01-01T00:00:00Z"}' > .session_id
|
echo '{"session_id": "session-old", "timestamp": "2020-01-01T00:00:00Z"}' > .claude_session_id
|
||||||
|
|
||||||
run should_resume_session
|
run should_resume_session
|
||||||
|
|
||||||
|
|
@ -730,7 +730,7 @@ EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "should_resume_session returns false when no session file" {
|
@test "should_resume_session returns false when no session file" {
|
||||||
rm -f .session_id
|
rm -f .claude_session_id
|
||||||
|
|
||||||
run should_resume_session
|
run should_resume_session
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue