mirror of
https://github.com/anthropics/claude-plugins-official.git
synced 2026-03-20 11:33:08 +00:00
fix(ralph-loop): isolate loop state to the session that started it
The state file lives at .claude/ralph-loop.local.md — project-scoped, not session-scoped. The plugin's Stop hook fires in every Claude Code session open in that project directory. So if session A starts a loop, session B's Stop events also find the state file and block, feeding A's prompt into B and consuming A's iteration budget. This was masked by the transcript-parsing bug fixed in the previous commit: that bug deleted the state file on the first Stop in any session, so neither session looped. Fixing it exposed the leak. Fix: setup writes CLAUDE_CODE_SESSION_ID into the frontmatter; the hook compares against .session_id from its stdin JSON and exits silently on mismatch. State files without session_id (written by old setup scripts) fall through to preserve existing behavior.
This commit is contained in:
@@ -24,6 +24,16 @@ MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iter
|
|||||||
# Extract completion_promise and strip surrounding quotes if present
|
# Extract completion_promise and strip surrounding quotes if present
|
||||||
COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/')
|
COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/')
|
||||||
|
|
||||||
|
# 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 // ""')
|
||||||
|
if [[ -n "$STATE_SESSION" ]] && [[ "$STATE_SESSION" != "$HOOK_SESSION" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Validate numeric fields before arithmetic operations
|
# Validate numeric fields before arithmetic operations
|
||||||
if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
|
if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
|
||||||
echo "⚠️ Ralph loop: State file corrupted" >&2
|
echo "⚠️ Ralph loop: State file corrupted" >&2
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ cat > .claude/ralph-loop.local.md <<EOF
|
|||||||
---
|
---
|
||||||
active: true
|
active: true
|
||||||
iteration: 1
|
iteration: 1
|
||||||
|
session_id: ${CLAUDE_CODE_SESSION_ID:-}
|
||||||
max_iterations: $MAX_ITERATIONS
|
max_iterations: $MAX_ITERATIONS
|
||||||
completion_promise: $COMPLETION_PROMISE_YAML
|
completion_promise: $COMPLETION_PROMISE_YAML
|
||||||
started_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
started_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
|||||||
Reference in New Issue
Block a user