diff --git a/plugins/ralph-loop/hooks/stop-hook.sh b/plugins/ralph-loop/hooks/stop-hook.sh index 7edf13b7..6873772d 100755 --- a/plugins/ralph-loop/hooks/stop-hook.sh +++ b/plugins/ralph-loop/hooks/stop-hook.sh @@ -27,9 +27,19 @@ COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/ # Session isolation: the state file is project-scoped, but the Stop hook # fires in every Claude Code session in that project. If another session # started the loop, this session must not block (or touch the state file). -# Legacy state files without session_id fall through (preserves old behavior). STATE_SESSION=$(echo "$FRONTMATTER" | grep '^session_id:' | sed 's/session_id: *//' || true) HOOK_SESSION=$(echo "$HOOK_INPUT" | jq -r '.session_id // ""') + +# Lazy claim: CLAUDE_CODE_SESSION_ID env var is not available to plugin +# scripts at setup time, but the stop hook receives session_id via stdin +# JSON. On first invocation, claim the loop for the current session. +if [[ -z "$STATE_SESSION" ]] && [[ -n "$HOOK_SESSION" ]]; then + TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$" + sed "s/^session_id:.*/session_id: $HOOK_SESSION/" "$RALPH_STATE_FILE" > "$TEMP_FILE" + mv "$TEMP_FILE" "$RALPH_STATE_FILE" + STATE_SESSION="$HOOK_SESSION" +fi + if [[ -n "$STATE_SESSION" ]] && [[ "$STATE_SESSION" != "$HOOK_SESSION" ]]; then exit 0 fi