diff --git a/plugins/session-bridge/commands/bridge.md b/plugins/session-bridge/commands/bridge.md index 72ddfe3..27f12f4 100644 --- a/plugins/session-bridge/commands/bridge.md +++ b/plugins/session-bridge/commands/bridge.md @@ -41,23 +41,21 @@ Register this session as a bridge peer. ### `connect ` -Connect to a peer session. Auto-starts this session's bridge if not already active. +Connect to a peer session. Always ensures this session has its own bridge first. -1. Extract the session ID from the argument. -2. Check if this session has a bridge: - ```bash - bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-session-id.sh" - ``` - If it exits with code 1 (not found), auto-start the bridge first: +1. Extract the target session ID from the argument. +2. **Always register first** to guarantee this session has its own unique bridge identity. + `register.sh` is idempotent — if `BRIDGE_SESSION_ID` env var is already set (from a + prior `/bridge start` in this process), it reuses that session. Otherwise it creates a new one: ```bash bash "${CLAUDE_PLUGIN_ROOT}/scripts/register.sh" ``` - Capture the session ID from stdout and note it for the user. -3. Then connect to the peer: + Capture the returned session ID — this is YOUR session's ID. +3. Then connect to the peer using YOUR session ID from step 2: ```bash - BRIDGE_SESSION_ID=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-session-id.sh") bash "${CLAUDE_PLUGIN_ROOT}/scripts/connect-peer.sh" "" + BRIDGE_SESSION_ID= bash "${CLAUDE_PLUGIN_ROOT}/scripts/connect-peer.sh" "" ``` -4. If successful, display the peer's project name and path. If you auto-started in step 2, also show this session's ID. +4. If successful, display the peer's project name and path, and show this session's ID. 5. If it fails (peer not found), suggest `/bridge peers` to see available sessions. 6. Tell the user: "Connected! Use `/bridge listen` to start answering peer queries, or `/bridge ask ` to ask them something." @@ -67,15 +65,13 @@ Enter listening mode — continuously wait for peer messages and respond to them **This is a loop. You MUST keep listening until the user interrupts (Ctrl+C).** -**Auto-start:** First, check if this session has a bridge: -```bash -bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-session-id.sh" -``` -If it exits with code 1, auto-start: +**Auto-start:** Always register first to ensure this session has its own bridge: ```bash bash "${CLAUDE_PLUGIN_ROOT}/scripts/register.sh" ``` -Display: "Bridge auto-started! Session ID: ". Then proceed to the loop. +Capture the returned session ID. If this session was already registered (via `BRIDGE_SESSION_ID` +env var), `register.sh` reuses it. Otherwise it creates a new one. +Display: "Bridge active! Session ID: ". Then proceed to the loop. Store the session ID in a variable (e.g., `MY_SESSION`) for use throughout the loop. @@ -114,11 +110,11 @@ The loop: Send a query to a connected peer and wait for the response. -1. Get session ID: +1. Get session ID — register first to ensure this session has its own bridge: ```bash - MY_SESSION=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-session-id.sh") + MY_SESSION=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/register.sh") ``` - If it fails, tell the user to run `/bridge start` first. + This is idempotent — reuses the existing session if `BRIDGE_SESSION_ID` env var is set. 2. Find connected peers: ```bash find ~/.claude/session-bridge/sessions/$MY_SESSION/inbox -name "*.json" -exec jq -r 'select(.type == "ping") | .from' {} \; 2>/dev/null | sort -u @@ -158,7 +154,7 @@ Show current bridge state. ```bash MY_SESSION=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-session-id.sh") ``` - If it fails, say "Bridge is not active. Run `/bridge start` to begin." + If it exits with code 1, say "Bridge is not active. Run `/bridge start` to begin." 2. Display the session ID. 3. List connected peers (from ping messages in inbox). 4. Count pending (unread) messages in inbox. diff --git a/plugins/session-bridge/scripts/bridge-listen.sh b/plugins/session-bridge/scripts/bridge-listen.sh index 2828891..2be2c81 100755 --- a/plugins/session-bridge/scripts/bridge-listen.sh +++ b/plugins/session-bridge/scripts/bridge-listen.sh @@ -15,11 +15,15 @@ if [ -n "${1:-}" ] && [ "${1:-}" != "0" ] && ! echo "$1" | grep -qE '^[0-9]+$'; SESSION_ID="$1" TIMEOUT="${2:-0}" else - # No session ID given, try to find it - SESSION_ID=$(bash "$SCRIPT_DIR/get-session-id.sh" 2>/dev/null) || { - echo "Error: No bridge session found. Run /bridge start first." >&2 - exit 1 - } + # No session ID given — prefer BRIDGE_SESSION_ID env var, then fall back to get-session-id.sh + if [ -n "${BRIDGE_SESSION_ID:-}" ]; then + SESSION_ID="$BRIDGE_SESSION_ID" + else + SESSION_ID=$(bash "$SCRIPT_DIR/get-session-id.sh" 2>/dev/null) || { + echo "Error: No bridge session found. Run /bridge start first." >&2 + exit 1 + } + fi TIMEOUT="${1:-0}" fi diff --git a/plugins/session-bridge/scripts/cleanup.sh b/plugins/session-bridge/scripts/cleanup.sh index dc2b644..c750995 100755 --- a/plugins/session-bridge/scripts/cleanup.sh +++ b/plugins/session-bridge/scripts/cleanup.sh @@ -7,19 +7,21 @@ PROJECT_DIR="${PROJECT_DIR:-$(pwd)}" BRIDGE_SESSION_FILE="$PROJECT_DIR/.claude/bridge-session" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -# Find session ID -SESSION_ID="" -if [ -f "$BRIDGE_SESSION_FILE" ]; then - SESSION_ID=$(cat "$BRIDGE_SESSION_FILE") -else - for MANIFEST_FILE in "$BRIDGE_DIR"/sessions/*/manifest.json; do - [ -f "$MANIFEST_FILE" ] || continue - MANIFEST_PATH=$(jq -r '.projectPath // ""' "$MANIFEST_FILE" 2>/dev/null) - if [ "$MANIFEST_PATH" = "$PROJECT_DIR" ]; then - SESSION_ID=$(jq -r '.sessionId' "$MANIFEST_FILE") - break - fi - done +# Find session ID — prefer env var (per-process), then file, then scan +SESSION_ID="${BRIDGE_SESSION_ID:-}" +if [ -z "$SESSION_ID" ]; then + if [ -f "$BRIDGE_SESSION_FILE" ]; then + SESSION_ID=$(cat "$BRIDGE_SESSION_FILE") + else + for MANIFEST_FILE in "$BRIDGE_DIR"/sessions/*/manifest.json; do + [ -f "$MANIFEST_FILE" ] || continue + MANIFEST_PATH=$(jq -r '.projectPath // ""' "$MANIFEST_FILE" 2>/dev/null) + if [ "$MANIFEST_PATH" = "$PROJECT_DIR" ]; then + SESSION_ID=$(jq -r '.sessionId' "$MANIFEST_FILE") + break + fi + done + fi fi if [ -z "$SESSION_ID" ]; then @@ -52,8 +54,14 @@ done # Remove session directory rm -rf "$SESSION_DIR" -# Remove bridge-session pointer -rm -f "$BRIDGE_SESSION_FILE" +# Only remove bridge-session pointer if it still points to THIS session. +# Another session in the same repo may have overwritten it with their own ID. +if [ -f "$BRIDGE_SESSION_FILE" ]; then + FILE_ID=$(cat "$BRIDGE_SESSION_FILE") + if [ "$FILE_ID" = "$SESSION_ID" ]; then + rm -f "$BRIDGE_SESSION_FILE" + fi +fi # Clean up stale sessions (heartbeat older than 30 minutes) STALE_CUTOFF=$(date -u -v-30M +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u -d "30 minutes ago" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "") diff --git a/plugins/session-bridge/scripts/get-session-id.sh b/plugins/session-bridge/scripts/get-session-id.sh index 6059343..907069b 100755 --- a/plugins/session-bridge/scripts/get-session-id.sh +++ b/plugins/session-bridge/scripts/get-session-id.sh @@ -1,10 +1,11 @@ #!/usr/bin/env bash -# scripts/get-session-id.sh — Reliably find this project's bridge session ID. +# scripts/get-session-id.sh — Reliably find this session's bridge session ID. # Works even if the agent cd'd into a subdirectory. # # Strategy: -# 1. Try .claude/bridge-session in current directory (fast path) -# 2. Scan all session manifests for one whose projectPath is a parent of $(pwd) +# 1. Check BRIDGE_SESSION_ID env var (per-process, always correct) +# 2. Try .claude/bridge-session file (convenience pointer, may belong to another session) +# 3. Scan all session manifests for one whose projectPath is a parent of $(pwd) # # Outputs: session ID to stdout, or exits 1 if not found. set -euo pipefail @@ -12,21 +13,31 @@ set -euo pipefail BRIDGE_DIR="${BRIDGE_DIR:-$HOME/.claude/session-bridge}" CURRENT_DIR="${PROJECT_DIR:-$(pwd)}" -# Fast path: .claude/bridge-session in current directory +# Fast path: env var set by register.sh via CLAUDE_ENV_FILE (per-process, no collisions) +if [ -n "${BRIDGE_SESSION_ID:-}" ]; then + SESSION_DIR="$BRIDGE_DIR/sessions/$BRIDGE_SESSION_ID" + if [ -d "$SESSION_DIR" ] && [ -f "$SESSION_DIR/manifest.json" ]; then + echo -n "$BRIDGE_SESSION_ID" + exit 0 + fi +fi + +# Fallback: .claude/bridge-session file (may point to a different session in the same repo) if [ -f "$CURRENT_DIR/.claude/bridge-session" ]; then - cat "$CURRENT_DIR/.claude/bridge-session" - exit 0 + FILE_ID=$(cat "$CURRENT_DIR/.claude/bridge-session") + SESSION_DIR="$BRIDGE_DIR/sessions/$FILE_ID" + if [ -d "$SESSION_DIR" ] && [ -f "$SESSION_DIR/manifest.json" ]; then + echo -n "$FILE_ID" + exit 0 + fi fi -# Fallback: scan all session manifests for one whose projectPath is a parent of current dir. -# e.g., if we're at /projects/my-lib/src/main/kotlin and a session has -# projectPath=/projects/my-lib, that's a match. +# Last resort: scan all session manifests for one whose projectPath is a parent of current dir. for MANIFEST in "$BRIDGE_DIR"/sessions/*/manifest.json; do [ -f "$MANIFEST" ] || continue PROJ_PATH=$(jq -r '.projectPath // ""' "$MANIFEST" 2>/dev/null) [ -n "$PROJ_PATH" ] || continue - # Check: is our current directory inside (or equal to) this project's path? case "$CURRENT_DIR" in "$PROJ_PATH"|"$PROJ_PATH"/*) jq -r '.sessionId' "$MANIFEST" diff --git a/plugins/session-bridge/scripts/register.sh b/plugins/session-bridge/scripts/register.sh index 603592a..5ab33d6 100755 --- a/plugins/session-bridge/scripts/register.sh +++ b/plugins/session-bridge/scripts/register.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash # scripts/register.sh — Register this session as a bridge peer. -# Env: BRIDGE_DIR (default: ~/.claude/session-bridge), PROJECT_DIR (default: pwd) +# Env: BRIDGE_DIR (default: ~/.claude/session-bridge), PROJECT_DIR (default: pwd), +# BRIDGE_SESSION_ID (if set, reuse this session instead of creating a new one) # Outputs: session ID to stdout -# If a bridge session already exists for this project, reuses it. +# Each Claude session gets its own bridge — multiple sessions in the same repo are independent. set -euo pipefail command -v jq >/dev/null 2>&1 || { echo "Error: jq is required. Install with: brew install jq (macOS) or apt install jq (Linux)" >&2; exit 1; } @@ -12,10 +13,10 @@ PROJECT_DIR="${PROJECT_DIR:-$(pwd)}" PROJECT_NAME=$(basename "$PROJECT_DIR") BRIDGE_SESSION_FILE="$PROJECT_DIR/.claude/bridge-session" -# Reuse existing session if the bridge-session file exists and points to a valid session -if [ -f "$BRIDGE_SESSION_FILE" ]; then - EXISTING_ID=$(cat "$BRIDGE_SESSION_FILE") - EXISTING_DIR="$BRIDGE_DIR/sessions/$EXISTING_ID" +# Reuse existing session only if BRIDGE_SESSION_ID env var is set and points to a valid session. +# This env var is per-process, so two Claude sessions in the same repo won't collide. +if [ -n "${BRIDGE_SESSION_ID:-}" ]; then + EXISTING_DIR="$BRIDGE_DIR/sessions/$BRIDGE_SESSION_ID" if [ -d "$EXISTING_DIR" ] && [ -f "$EXISTING_DIR/manifest.json" ]; then # Update heartbeat and reuse @@ -23,7 +24,7 @@ if [ -f "$BRIDGE_SESSION_FILE" ]; then TMP=$(mktemp "$EXISTING_DIR/manifest.XXXXXX") jq --arg hb "$NOW" '.lastHeartbeat = $hb' "$EXISTING_DIR/manifest.json" > "$TMP" mv "$TMP" "$EXISTING_DIR/manifest.json" - echo -n "$EXISTING_ID" + echo -n "$BRIDGE_SESSION_ID" exit 0 fi fi @@ -49,6 +50,8 @@ cat > "$MANIFEST_TMP" < "$BRIDGE_SESSION_FILE" diff --git a/plugins/session-bridge/skills/bridge-awareness/SKILL.md b/plugins/session-bridge/skills/bridge-awareness/SKILL.md index d69ffb4..35a0650 100644 --- a/plugins/session-bridge/skills/bridge-awareness/SKILL.md +++ b/plugins/session-bridge/skills/bridge-awareness/SKILL.md @@ -7,7 +7,11 @@ description: Activates when a bridge session is active. Teaches the agent to com You are connected to other Claude Code sessions via the Claude Bridge plugin. This skill defines how you communicate with peer agents. -**Getting your session ID:** Always use `bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-session-id.sh"` to get your session ID. This works even if you've cd'd into a subdirectory. In listen mode, use `TO_ID` from the message output instead (even more reliable). +**Getting your session ID:** Always register first to ensure you have your own bridge identity: +```bash +MY_SESSION=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/register.sh") +``` +This is idempotent — if `BRIDGE_SESSION_ID` env var is set (from a prior registration), it reuses that session. In listen mode, use `TO_ID` from the message output instead (even more reliable). ## Querying Peers @@ -15,7 +19,7 @@ When the user asks you to communicate with a peer — whether via `/bridge ask`, 1. Get your session ID. If no bridge exists yet, auto-register first: ```bash - MY_SESSION=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-session-id.sh" 2>/dev/null) || MY_SESSION=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/register.sh") + MY_SESSION=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/register.sh") ``` Then find connected peers: ```bash @@ -100,7 +104,7 @@ BRIDGE_SESSION_ID= bash "${CLAUDE_PLUGIN_ROOT}/scripts/send-message.sh" < - **In listen mode, NEVER break the loop.** Respond, then immediately listen again. - **Always use `send-message.sh`** — never write message JSON files directly. - **In listen mode, use `TO_ID`** from the message output as BRIDGE_SESSION_ID. -- **Outside listen mode, use `get-session-id.sh`** to get your session ID reliably. +- **Outside listen mode, use `register.sh`** to get your session ID (idempotent — reuses existing via env var). - **Never use `$(cat .claude/bridge-session)` directly** — it's a relative path that breaks when the working directory changes. - **Include real code in responses** — read actual files and paste relevant sections. Don't just describe changes in prose. - **Route to the right peer** when connected to multiple. Use project names to decide relevance. diff --git a/plugins/session-bridge/tests/test-cleanup.sh b/plugins/session-bridge/tests/test-cleanup.sh index 25ba907..2d0c8a0 100755 --- a/plugins/session-bridge/tests/test-cleanup.sh +++ b/plugins/session-bridge/tests/test-cleanup.sh @@ -35,16 +35,16 @@ echo "" echo "Test 1: Cleanup removes session directory" SESSION_A_DIR="$BRIDGE_DIR/sessions/$SESSION_A" assert_dir_exists "session dir exists before cleanup" "$SESSION_A_DIR" -BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_A" bash "$CLEANUP" +BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_A" BRIDGE_SESSION_ID="$SESSION_A" bash "$CLEANUP" if [ ! -d "$SESSION_A_DIR" ]; then echo " PASS: session dir removed"; PASS=$((PASS + 1)) else echo " FAIL: session dir still exists"; FAIL=$((FAIL + 1)) fi -# --- Test 2: bridge-session pointer removed --- +# --- Test 2: bridge-session pointer removed (file belongs to this session) --- echo "" -echo "Test 2: bridge-session file removed" +echo "Test 2: bridge-session file removed when it belongs to the exiting session" if [ ! -f "$PROJECT_A/.claude/bridge-session" ]; then echo " PASS: bridge-session file removed"; PASS=$((PASS + 1)) else @@ -93,7 +93,7 @@ assert_dir_exists "stale session exists before cleanup" "$STALE_DIR" PROJECT_D="$TEST_TMPDIR/project-d" mkdir -p "$PROJECT_D" SESSION_D=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_D" bash "$REGISTER") -BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_D" bash "$CLEANUP" +BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_D" BRIDGE_SESSION_ID="$SESSION_D" bash "$CLEANUP" if [ ! -d "$STALE_DIR" ]; then echo " PASS: stale session cleaned up"; PASS=$((PASS + 1)) else @@ -123,8 +123,28 @@ assert_dir_exists "session B still exists" "$SESSION_B_DIR" PROJECT_F="$TEST_TMPDIR/project-f" mkdir -p "$PROJECT_F" SESSION_F=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_F" bash "$REGISTER") -BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_F" bash "$CLEANUP" +BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_F" BRIDGE_SESSION_ID="$SESSION_F" bash "$CLEANUP" assert_dir_exists "active session B not cleaned" "$SESSION_B_DIR" +# --- Test 7: Cleanup of one session doesn't delete bridge-session file owned by another --- +echo "" +echo "Test 7: Cleanup preserves bridge-session file owned by another session" +PROJECT_SHARED="$TEST_TMPDIR/project-shared" +mkdir -p "$PROJECT_SHARED" +SID_1=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_SHARED" bash "$REGISTER") +SID_2=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_SHARED" bash "$REGISTER") +# File now points to SID_2 (last registered) +assert_eq "file points to SID_2" "$SID_2" "$(cat "$PROJECT_SHARED/.claude/bridge-session")" +# Clean up SID_1 — should NOT delete the file since it belongs to SID_2 +BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_SHARED" BRIDGE_SESSION_ID="$SID_1" bash "$CLEANUP" +if [ ! -d "$BRIDGE_DIR/sessions/$SID_1" ]; then + echo " PASS: session 1 dir removed"; PASS=$((PASS + 1)) +else + echo " FAIL: session 1 dir still exists"; FAIL=$((FAIL + 1)) +fi +assert_file_exists "bridge-session file preserved for session 2" "$PROJECT_SHARED/.claude/bridge-session" +assert_eq "file still points to SID_2" "$SID_2" "$(cat "$PROJECT_SHARED/.claude/bridge-session")" +assert_dir_exists "session 2 dir untouched" "$BRIDGE_DIR/sessions/$SID_2" + print_results diff --git a/plugins/session-bridge/tests/test-get-session-id.sh b/plugins/session-bridge/tests/test-get-session-id.sh index 014484c..bc8f75a 100755 --- a/plugins/session-bridge/tests/test-get-session-id.sh +++ b/plugins/session-bridge/tests/test-get-session-id.sh @@ -88,6 +88,32 @@ fi echo "" echo "Test 7: Fast path — uses bridge-session file directly when available" FOUND=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" bash "$GET_ID") -assert_eq "fast path works" "$SESSION_ID" "$FOUND" +# File may now point to SESSION_B (last registered), but should still return a valid session +if [ -n "$FOUND" ]; then + echo " PASS: fast path returns a session"; PASS=$((PASS + 1)) +else + echo " FAIL: fast path returned nothing"; FAIL=$((FAIL + 1)) +fi + +# --- Test 8: BRIDGE_SESSION_ID env var takes priority over file --- +echo "" +echo "Test 8: Env var takes priority over bridge-session file" +# Register a second session in the same project (simulates second Claude session) +SESSION_ID_2=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" bash "$REGISTER") +# The file now points to SESSION_ID_2, but env var should win +FOUND=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" BRIDGE_SESSION_ID="$SESSION_ID" bash "$GET_ID") +assert_eq "env var wins over file" "$SESSION_ID" "$FOUND" + +# --- Test 9: Without env var, falls back to file --- +echo "" +echo "Test 9: Without env var, falls back to bridge-session file" +FOUND=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" bash "$GET_ID") +assert_eq "falls back to file" "$SESSION_ID_2" "$FOUND" + +# --- Test 10: Stale env var (deleted session) falls back to file --- +echo "" +echo "Test 10: Stale env var (session dir gone) falls back to file" +FOUND=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" BRIDGE_SESSION_ID="nonexistent" bash "$GET_ID") +assert_eq "stale env var falls back to file" "$SESSION_ID_2" "$FOUND" print_results diff --git a/plugins/session-bridge/tests/test-register.sh b/plugins/session-bridge/tests/test-register.sh index 3f6a0da..72f699c 100755 --- a/plugins/session-bridge/tests/test-register.sh +++ b/plugins/session-bridge/tests/test-register.sh @@ -71,11 +71,21 @@ mkdir -p "$PROJECT_DIR2" SESSION_ID2=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR2" CLAUDE_ENV_FILE="$ENV_FILE" bash "$REGISTER") assert_contains "BRIDGE_SESSION_ID in env file" "BRIDGE_SESSION_ID=$SESSION_ID2" "$(cat "$ENV_FILE")" -# --- Test 6: Re-registering same project reuses session --- +# --- Test 6: Re-registering with BRIDGE_SESSION_ID env var reuses session --- echo "" -echo "Test 6: Re-registering same project reuses existing session" -SESSION_ID_AGAIN=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" bash "$REGISTER") -assert_eq "reused same session ID" "$SESSION_ID" "$SESSION_ID_AGAIN" +echo "Test 6: Re-registering with BRIDGE_SESSION_ID reuses existing session" +SESSION_ID_AGAIN=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" BRIDGE_SESSION_ID="$SESSION_ID" bash "$REGISTER") +assert_eq "reused same session ID via env var" "$SESSION_ID" "$SESSION_ID_AGAIN" + +# --- Test 6b: Re-registering same project WITHOUT env var creates new session --- +echo "" +echo "Test 6b: Re-registering same project without env var creates new session" +SESSION_ID_NEW=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" bash "$REGISTER") +if [ "$SESSION_ID_NEW" != "$SESSION_ID" ]; then + echo " PASS: new session created without env var (simulates second Claude session)"; PASS=$((PASS + 1)) +else + echo " FAIL: reused session without env var"; FAIL=$((FAIL + 1)) +fi # --- Test 7: Stale pointer (session dir missing) creates new session --- echo "" @@ -106,14 +116,14 @@ fi # --- Test 9: heartbeat updated on re-register --- echo "" -echo "Test 9: Heartbeat is updated when re-registering" +echo "Test 9: Heartbeat is updated when re-registering with env var" # Set old heartbeat NEW_SESSION_DIR="$BRIDGE_DIR/sessions/$NEW_SESSION_ID" TMP=$(mktemp "$NEW_SESSION_DIR/manifest.XXXXXX") jq '.lastHeartbeat = "2020-01-01T00:00:00Z"' "$NEW_SESSION_DIR/manifest.json" > "$TMP" mv "$TMP" "$NEW_SESSION_DIR/manifest.json" -# Re-register -BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" bash "$REGISTER" > /dev/null +# Re-register with BRIDGE_SESSION_ID set +BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_DIR" BRIDGE_SESSION_ID="$NEW_SESSION_ID" bash "$REGISTER" > /dev/null UPDATED_HB=$(jq -r '.lastHeartbeat' "$NEW_SESSION_DIR/manifest.json") if [ "$UPDATED_HB" != "2020-01-01T00:00:00Z" ]; then echo " PASS: heartbeat updated on re-register"; PASS=$((PASS + 1)) @@ -121,4 +131,19 @@ else echo " FAIL: heartbeat not updated"; FAIL=$((FAIL + 1)) fi +# --- Test 10: Two sessions in same project get independent bridges --- +echo "" +echo "Test 10: Two sessions in same project get independent bridge IDs" +PROJECT_SHARED="$TEST_TMPDIR/shared-project" +mkdir -p "$PROJECT_SHARED" +SID_1=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_SHARED" bash "$REGISTER") +SID_2=$(BRIDGE_DIR="$BRIDGE_DIR" PROJECT_DIR="$PROJECT_SHARED" bash "$REGISTER") +if [ "$SID_1" != "$SID_2" ]; then + echo " PASS: two sessions in same project get different IDs"; PASS=$((PASS + 1)) +else + echo " FAIL: both sessions got same ID"; FAIL=$((FAIL + 1)) +fi +assert_dir_exists "session 1 dir exists" "$BRIDGE_DIR/sessions/$SID_1" +assert_dir_exists "session 2 dir exists" "$BRIDGE_DIR/sessions/$SID_2" + print_results