From 0c995c4ce9340852106f6154ea5c6e1564ce2f2e Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 25 Mar 2026 15:02:21 +0900 Subject: [PATCH] fix(ralph-loop): lazy-claim session_id for cross-session isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLAUDE_CODE_SESSION_ID env var is not available to plugin setup scripts, so ralph-loop state files are created with an empty session_id. This causes the stop hook to fire in ALL sessions within the same project, not just the one that started the loop. The stop hook already receives session_id via stdin JSON. This change adds a "lazy claim" — on first stop hook invocation, if the state file has no session_id, the hook writes the current session's ID into the state file. Subsequent invocations from other sessions see a mismatch and pass through. Co-Authored-By: Claude Opus 4.6 (1M context) --- plugins/ralph-loop/hooks/stop-hook.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/ralph-loop/hooks/stop-hook.sh b/plugins/ralph-loop/hooks/stop-hook.sh index 7edf13b72..6873772d6 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