Skip to content

fix(main): requestSingleInstanceLock to prevent PTY loss on replace - #56

Merged
abasiri merged 2 commits into
doctly:mainfrom
JeanBaptisteRenard:upstream/fix-single-instance-lock
Aug 1, 2026
Merged

fix(main): requestSingleInstanceLock to prevent PTY loss on replace#56
abasiri merged 2 commits into
doctly:mainfrom
JeanBaptisteRenard:upstream/fix-single-instance-lock

Conversation

@JeanBaptisteRenard

Copy link
Copy Markdown
Contributor

Closes #31.

Problem

Replacing the installed binary (e.g. an AppImage on Linux) while Switchboard has active node-pty sessions can kill or orphan those sessions. The OS launches the new binary, which initialises a second Electron process; the two race on user-data and PTY pipes.

I hit this on 2026-05-23 when overwriting ~/Applications/Switchboard.AppImage while a Claude CLI session was running — the running PTY died silently and the renderer reconnected to nothing.

Fix

Electron's standard app.requestSingleInstanceLock():

  • The first instance acquires the lock and continues normally.
  • Any subsequent launch fails to acquire the lock, calls app.quit() immediately, and exits without touching the existing process or its PTYs.
  • A 'second-instance' listener on the first instance brings its main window to the front, so the user sees the existing app instead of nothing happening.

The whole existing app.whenReady().then(...) block is wrapped in the else branch so it only runs for the genuine first instance.

Issue link

Also requested in #31 ("Feature request: single-instance mode (focus existing window when re-launched)") — same mechanism solves both the data-loss case and the UX issue.

Tested on

Linux / AppImage. The mechanism is platform-agnostic; requestSingleInstanceLock is documented for all three desktop platforms.

Notes

  • I have a fork branch off this change that touches more files (worktree dialog, subagent observability, etc.). This PR is intentionally minimal — just the single-instance lock so it can land independently.
  • If you'd like a test for the second-instance path I can add one, though it's tricky to spawn a second Electron in a unit test; manual repro is the easiest validation.

JeanBaptisteRenard and others added 2 commits May 30, 2026 03:08
… replace

On 2026-05-23, replacing the AppImage while Switchboard had active node-pty
sessions killed those sessions. The OS spawned the new binary which initialised
a second Electron process; the two instances raced and the running PTYs were
orphaned/killed.

Electron's requestSingleInstanceLock() is the standard fix:
- The first instance acquires the lock and continues normally.
- Any subsequent launch (e.g. the new AppImage binary after an in-place replace)
  fails to acquire the lock, calls app.quit() immediately, and exits without
  touching any PTY.
- A 'second-instance' listener on the first instance brings its main window to
  the front, so the user gets visual confirmation the app is still running.

Changes:
- main.js: call app.requestSingleInstanceLock() before app.whenReady()
- Wrap app.whenReady() and all init code in the else-branch so it only runs for
  the true first instance
- Register app.on('second-instance') to restore/focus mainWindow
Branch was 14 behind. The conflict was in the block this PR wraps: #32 landed
on main and changed runScheduleCommand from a shell string to argv
(runScheduleCommand(claudeArgv, …) plus quoteArgvForShell). This branch still
carried the pre-#32 string form inside its new single-instance wrapper, so a
naive resolution would have silently reverted the shell-injection fix.

Resolved by keeping main's argv-based whenReady body and wrapping it in this
PR's requestSingleInstanceLock else-branch. Verified with `git diff -w` against
main: the only semantic change is the lock; everything else is the one-level
reindent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@abasiri
abasiri merged commit 35a25e5 into doctly:main Aug 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: single-instance mode (focus existing window when re-launched)

2 participants