fix(session): address code review feedback for session management
- Fix SC2155: separate declare from assign in get_session_id(), log_session_transition(), init_session_tracking() - Use jq for safe JSON generation in reset_session() and init_session_tracking() instead of heredocs (prevents special character issues) - Add corruption tolerance to log_session_transition() with JSON validation and fallback to empty array on parse failures - Add generate_session_id() to create unique session IDs (ralph-<epoch>-<random>) - Add update_session_last_used() helper called on each loop iteration - init_session_tracking() now generates unique session_id and sets last_used - Add session files to .gitignore (.ralph_session, .ralph_session_history, .claude_session_id) All 265 tests pass.
This commit is contained in:
parent
274f496f77
commit
cafaacdc0c
2 changed files with 133 additions and 44 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,6 +3,9 @@
|
||||||
.last_reset
|
.last_reset
|
||||||
.exit_signals
|
.exit_signals
|
||||||
status.json
|
status.json
|
||||||
|
.ralph_session
|
||||||
|
.ralph_session_history
|
||||||
|
.claude_session_id
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs/*
|
logs/*
|
||||||
|
|
|
||||||
174
ralph_loop.sh
174
ralph_loop.sh
|
|
@ -494,9 +494,13 @@ get_session_id() {
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract session_id from JSON file
|
# Extract session_id from JSON file (SC2155: separate declare from assign)
|
||||||
local session_id=$(jq -r '.session_id // ""' "$RALPH_SESSION_FILE" 2>/dev/null)
|
local session_id
|
||||||
if [[ "$session_id" == "null" ]]; then
|
session_id=$(jq -r '.session_id // ""' "$RALPH_SESSION_FILE" 2>/dev/null)
|
||||||
|
local jq_status=$?
|
||||||
|
|
||||||
|
# Handle jq failure or null/empty results
|
||||||
|
if [[ $jq_status -ne 0 || -z "$session_id" || "$session_id" == "null" ]]; then
|
||||||
session_id=""
|
session_id=""
|
||||||
fi
|
fi
|
||||||
echo "$session_id"
|
echo "$session_id"
|
||||||
|
|
@ -508,22 +512,30 @@ get_session_id() {
|
||||||
reset_session() {
|
reset_session() {
|
||||||
local reason=${1:-"manual_reset"}
|
local reason=${1:-"manual_reset"}
|
||||||
|
|
||||||
# Always create/overwrite the session file to ensure consistent state
|
# Get current timestamp
|
||||||
cat > "$RALPH_SESSION_FILE" << EOF
|
local reset_timestamp
|
||||||
{
|
reset_timestamp=$(get_iso_timestamp)
|
||||||
"session_id": "",
|
|
||||||
"created_at": "",
|
# Always create/overwrite the session file using jq for safe JSON escaping
|
||||||
"last_used": "",
|
jq -n \
|
||||||
"reset_at": "$(get_iso_timestamp)",
|
--arg session_id "" \
|
||||||
"reset_reason": "$reason"
|
--arg created_at "" \
|
||||||
}
|
--arg last_used "" \
|
||||||
EOF
|
--arg reset_at "$reset_timestamp" \
|
||||||
|
--arg reset_reason "$reason" \
|
||||||
|
'{
|
||||||
|
session_id: $session_id,
|
||||||
|
created_at: $created_at,
|
||||||
|
last_used: $last_used,
|
||||||
|
reset_at: $reset_at,
|
||||||
|
reset_reason: $reset_reason
|
||||||
|
}' > "$RALPH_SESSION_FILE"
|
||||||
|
|
||||||
# Also clear the Claude session file for consistency
|
# Also clear the Claude session file for consistency
|
||||||
rm -f "$CLAUDE_SESSION_FILE" 2>/dev/null
|
rm -f "$CLAUDE_SESSION_FILE" 2>/dev/null
|
||||||
|
|
||||||
# Log the session transition
|
# Log the session transition (non-fatal to prevent script exit under set -e)
|
||||||
log_session_transition "active" "reset" "$reason" "${loop_count:-0}"
|
log_session_transition "active" "reset" "$reason" "${loop_count:-0}" || true
|
||||||
|
|
||||||
log_status "INFO" "Session reset: $reason"
|
log_status "INFO" "Session reset: $reason"
|
||||||
}
|
}
|
||||||
|
|
@ -536,15 +548,14 @@ log_session_transition() {
|
||||||
local reason=$3
|
local reason=$3
|
||||||
local loop_number=${4:-0}
|
local loop_number=${4:-0}
|
||||||
|
|
||||||
# Initialize history file if needed
|
# Get timestamp once (SC2155: separate declare from assign)
|
||||||
if [[ ! -f "$RALPH_SESSION_HISTORY_FILE" ]]; then
|
local ts
|
||||||
echo '[]' > "$RALPH_SESSION_HISTORY_FILE"
|
ts=$(get_iso_timestamp)
|
||||||
fi
|
|
||||||
|
|
||||||
# Create transition entry
|
# Create transition entry using jq for safe JSON (SC2155: separate declare from assign)
|
||||||
local transition
|
local transition
|
||||||
transition=$(jq -n \
|
transition=$(jq -n -c \
|
||||||
--arg timestamp "$(get_iso_timestamp)" \
|
--arg timestamp "$ts" \
|
||||||
--arg from_state "$from_state" \
|
--arg from_state "$from_state" \
|
||||||
--arg to_state "$to_state" \
|
--arg to_state "$to_state" \
|
||||||
--arg reason "$reason" \
|
--arg reason "$reason" \
|
||||||
|
|
@ -557,40 +568,107 @@ log_session_transition() {
|
||||||
loop_number: $loop_number
|
loop_number: $loop_number
|
||||||
}')
|
}')
|
||||||
|
|
||||||
# Append to history and keep only last 50 entries
|
# Read history file defensively - fallback to empty array on any failure
|
||||||
local history=$(cat "$RALPH_SESSION_HISTORY_FILE" 2>/dev/null || echo '[]')
|
local history
|
||||||
history=$(echo "$history" | jq ". += [$transition] | .[-50:]")
|
if [[ -f "$RALPH_SESSION_HISTORY_FILE" ]]; then
|
||||||
echo "$history" > "$RALPH_SESSION_HISTORY_FILE"
|
history=$(cat "$RALPH_SESSION_HISTORY_FILE" 2>/dev/null)
|
||||||
|
# Validate JSON, fallback to empty array if corrupted
|
||||||
|
if ! echo "$history" | jq empty 2>/dev/null; then
|
||||||
|
history='[]'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
history='[]'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Append transition and keep only last 50 entries
|
||||||
|
local updated_history
|
||||||
|
updated_history=$(echo "$history" | jq ". += [$transition] | .[-50:]" 2>/dev/null)
|
||||||
|
local jq_status=$?
|
||||||
|
|
||||||
|
# Only write if jq succeeded
|
||||||
|
if [[ $jq_status -eq 0 && -n "$updated_history" ]]; then
|
||||||
|
echo "$updated_history" > "$RALPH_SESSION_HISTORY_FILE"
|
||||||
|
else
|
||||||
|
# Fallback: start fresh with just this transition
|
||||||
|
echo "[$transition]" > "$RALPH_SESSION_HISTORY_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate a unique session ID using timestamp and random component
|
||||||
|
generate_session_id() {
|
||||||
|
local ts
|
||||||
|
ts=$(date +%s)
|
||||||
|
local rand
|
||||||
|
rand=$RANDOM
|
||||||
|
echo "ralph-${ts}-${rand}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize session tracking (called at loop start)
|
# Initialize session tracking (called at loop start)
|
||||||
init_session_tracking() {
|
init_session_tracking() {
|
||||||
|
local ts
|
||||||
|
ts=$(get_iso_timestamp)
|
||||||
|
|
||||||
# Create session file if it doesn't exist
|
# Create session file if it doesn't exist
|
||||||
if [[ ! -f "$RALPH_SESSION_FILE" ]]; then
|
if [[ ! -f "$RALPH_SESSION_FILE" ]]; then
|
||||||
cat > "$RALPH_SESSION_FILE" << EOF
|
local new_session_id
|
||||||
{
|
new_session_id=$(generate_session_id)
|
||||||
"session_id": "",
|
|
||||||
"created_at": "$(get_iso_timestamp)",
|
jq -n \
|
||||||
"last_used": "",
|
--arg session_id "$new_session_id" \
|
||||||
"reset_at": "",
|
--arg created_at "$ts" \
|
||||||
"reset_reason": ""
|
--arg last_used "$ts" \
|
||||||
}
|
--arg reset_at "" \
|
||||||
EOF
|
--arg reset_reason "" \
|
||||||
log_status "INFO" "Initialized session tracking"
|
'{
|
||||||
|
session_id: $session_id,
|
||||||
|
created_at: $created_at,
|
||||||
|
last_used: $last_used,
|
||||||
|
reset_at: $reset_at,
|
||||||
|
reset_reason: $reset_reason
|
||||||
|
}' > "$RALPH_SESSION_FILE"
|
||||||
|
|
||||||
|
log_status "INFO" "Initialized session tracking (session: $new_session_id)"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Validate existing session file
|
# Validate existing session file
|
||||||
if ! jq empty "$RALPH_SESSION_FILE" 2>/dev/null; then
|
if ! jq empty "$RALPH_SESSION_FILE" 2>/dev/null; then
|
||||||
log_status "WARN" "Corrupted session file detected, recreating..."
|
log_status "WARN" "Corrupted session file detected, recreating..."
|
||||||
cat > "$RALPH_SESSION_FILE" << EOF
|
local new_session_id
|
||||||
{
|
new_session_id=$(generate_session_id)
|
||||||
"session_id": "",
|
|
||||||
"created_at": "$(get_iso_timestamp)",
|
jq -n \
|
||||||
"last_used": "",
|
--arg session_id "$new_session_id" \
|
||||||
"reset_at": "",
|
--arg created_at "$ts" \
|
||||||
"reset_reason": "corrupted_file_recovery"
|
--arg last_used "$ts" \
|
||||||
|
--arg reset_at "$ts" \
|
||||||
|
--arg reset_reason "corrupted_file_recovery" \
|
||||||
|
'{
|
||||||
|
session_id: $session_id,
|
||||||
|
created_at: $created_at,
|
||||||
|
last_used: $last_used,
|
||||||
|
reset_at: $reset_at,
|
||||||
|
reset_reason: $reset_reason
|
||||||
|
}' > "$RALPH_SESSION_FILE"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
EOF
|
|
||||||
|
# Update last_used timestamp in session file (called on each loop iteration)
|
||||||
|
update_session_last_used() {
|
||||||
|
if [[ ! -f "$RALPH_SESSION_FILE" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ts
|
||||||
|
ts=$(get_iso_timestamp)
|
||||||
|
|
||||||
|
# Update last_used in existing session file
|
||||||
|
local updated
|
||||||
|
updated=$(jq --arg last_used "$ts" '.last_used = $last_used' "$RALPH_SESSION_FILE" 2>/dev/null)
|
||||||
|
local jq_status=$?
|
||||||
|
|
||||||
|
if [[ $jq_status -eq 0 && -n "$updated" ]]; then
|
||||||
|
echo "$updated" > "$RALPH_SESSION_FILE"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -899,6 +977,10 @@ main() {
|
||||||
while true; do
|
while true; do
|
||||||
loop_count=$((loop_count + 1))
|
loop_count=$((loop_count + 1))
|
||||||
log_status "INFO" "DEBUG: Successfully incremented loop_count to $loop_count"
|
log_status "INFO" "DEBUG: Successfully incremented loop_count to $loop_count"
|
||||||
|
|
||||||
|
# Update session last_used timestamp
|
||||||
|
update_session_last_used
|
||||||
|
|
||||||
log_status "INFO" "Loop #$loop_count - calling init_call_tracking..."
|
log_status "INFO" "Loop #$loop_count - calling init_call_tracking..."
|
||||||
init_call_tracking
|
init_call_tracking
|
||||||
|
|
||||||
|
|
@ -1031,6 +1113,10 @@ Files created:
|
||||||
- $LOG_DIR/: All execution logs
|
- $LOG_DIR/: All execution logs
|
||||||
- $DOCS_DIR/: Generated documentation
|
- $DOCS_DIR/: Generated documentation
|
||||||
- $STATUS_FILE: Current status (JSON)
|
- $STATUS_FILE: Current status (JSON)
|
||||||
|
- .ralph_session: Session lifecycle tracking
|
||||||
|
- .ralph_session_history: Session transition history (last 50)
|
||||||
|
- .call_count: API call counter for rate limiting
|
||||||
|
- .last_reset: Timestamp of last rate limit reset
|
||||||
|
|
||||||
Example workflow:
|
Example workflow:
|
||||||
ralph-setup my-project # Create project
|
ralph-setup my-project # Create project
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue