From ebea90d5f7b4071b71a55520707bbabd0bbc04fb Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Sun, 3 May 2026 14:08:15 +0200 Subject: [PATCH] u/complete: fix TOCTOU race spawning duplicate copilot sessions Use deterministic session ID derived from cwd hash instead of timestamp. Concurrent invocations for the same directory now converge on the same session name, and the server rejects the duplicate atomically. --- scripts/u/complete | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/scripts/u/complete b/scripts/u/complete index 364653f..237c6f1 100644 --- a/scripts/u/complete +++ b/scripts/u/complete @@ -46,28 +46,34 @@ done # otherwise spawns a new one. find_or_create_session() { local target_cwd="$1" - local sdir state scwd id kv deadline + local id kv deadline - while IFS= read -r line; do - id=${line%%$'\t'*} - case "$id" in *-copilot) ;; *) continue ;; esac - sdir="$MOUNT/s/$id" - [ -d "$sdir" ] || continue - scwd=$(cat "$sdir/cwd" 2>/dev/null) || continue - [ "$scwd" = "$target_cwd" ] || continue - state=$(cat "$sdir/state" 2>/dev/null) || continue - case "$state" in failed*|error*) continue ;; esac - printf '%s\n' "$id" - return 0 - done < "$MOUNT/s/idx" + # Deterministic name: one copilot session per cwd. + id="$(printf '%s' "$target_cwd" | cksum | awk '{print $1}')-copilot" + + if [ -d "$MOUNT/s/$id" ]; then + local state + state=$(cat "$MOUNT/s/$id/state" 2>/dev/null) || true + case "$state" in + failed*|error*) + # Dead session — kill and recreate below. + printf 'kill\n' > "$MOUNT/s/$id/ctl" 2>/dev/null || true + sleep 0.1 + ;; + *) + printf '%s\n' "$id" + return 0 + ;; + esac + fi - id="$(date +%s%N)-copilot" kv="name=${id} cwd=${target_cwd} agent=copilot backend=${backend} model=${model}" - printf '%s\n' "$kv" > "$MOUNT/s/new" || die "failed to create copilot session" + # If a concurrent invocation already created it, ignore the error. + printf '%s\n' "$kv" > "$MOUNT/s/new" 2>/dev/null || true deadline=$(( $(date +%s) + 5 )) until [ -d "$MOUNT/s/$id" ] || [ "$(date +%s)" -ge "$deadline" ]; do sleep 0.05; done