fix: share biometric session across parallel task runners; prevent duplicate daemons#754
Merged
Merged
Conversation
…plicate daemons
Two related fixes uncovered by running varlock through turborepo, where
parallel tasks each triggered their own biometric prompt and spawned
their own daemon (visible as multiple menu bar icons).
1. TTY-based session scoping (PeerIdentity.swift) now walks past
PTY-allocating wrappers (turbo, nx, pnpm/npm parallel, concurrently,
etc.) and anchors on the user's outer terminal TTY. Terminal
multiplexers (tmux, screen, zellij) are detected via env vars and
treated as a session boundary so per-pane scoping is preserved.
2. Daemon launch (IPCServer.swift, main.swift, daemon-client.ts) no
longer bypasses its own flock unconditionally — the old "stuck
recovery" path was the cause of two daemons coming up during a
parallel-spawn race. Flock failures are now triaged by lock age
(<5s = race, exit cleanly) and existing-daemon socket
responsiveness (responsive = healthy, exit; unresponsive + old =
stuck, take over). The polite-exit case emits {"alreadyRunning":
true} so the TS launcher resolves immediately instead of waiting
for the spawn timeout.
Contributor
|
The changes in this PR will be included in the next version bump.
|
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Two related fixes uncovered by running varlock through turborepo. When parallel tasks spawned
varlocksimultaneously, each task fired its own biometric prompt AND each spawned its own daemon (visible as multiple menu bar icons sticking around).1. TTY session scoping walks past PTY-allocating wrappers
PeerIdentity.swiftpreviously used the peer's immediate controlling TTY. Turborepo (and similar fan-out task runners like nx, pnpm/npm parallel scripts,concurrently, etc.) allocate a fresh PTY per task, so two parallel turbo tasks each resolved to a distincttty:ttysXXX:…session — meaning each one triggered its own biometric prompt.Fix: walk up the ancestry chain to the outermost ancestor that still has a TTY and anchor on that. In a plain shell this is a no-op (whole chain shares one TTY until the GUI terminal app, which has no TTY). With turbo et al. in the chain, we walk past their per-task PTYs to the user's terminal TTY. Terminal multiplexers (
tmux,screen,zellij) are detected via env vars (TMUX,STY,ZELLIJ) and treated as a session boundary so per-pane scoping is preserved.2. Daemon flock failure no longer unconditionally bypasses the lock
IPCServer.swiftpreviously detectedflockfailure and recovered by unlinking the lock file + opening a fresh inode. The comment said this was for "stuck UE state" recovery, but it also defeats the lock entirely during a parallel-spawn race — both daemons end up running.Fix: triage flock failures by two signals:
{"alreadyRunning": true}so the TS launcher resolves immediately and connects to the winner's socket.The two-signal check restores the stuck-daemon recovery path (the original intent) while no longer regressing the much more common parallel-spawn case.
daemon-client.tslearns to recognize thealreadyRunningmarker on stdout and resolves the spawn promise immediately instead of hanging until the 10s spawn timeout fires.Test plan
pnpm turbo run devin a 2-app workspace prompted twice and left two menu bar iconsvarlock lock && pnpm turbo run devstill produces one prompt (no regression on locked-state)tmuxwithvarlock loadin two panes still prompts separately (multiplexer env-var detection preserves per-pane scoping)varlock runin a single shell unchanged (TTY-walk is a no-op when whole chain shares one TTY)