diff --git a/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs b/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs index 93837d53..d7f5b80c 100644 --- a/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs @@ -657,7 +657,15 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable private void RestartWorkerService() { - var exe = _workerLocator.Find(); + // Prefer the exe path of the worker that's actually running: _workerLocator.Find() + // walks up from AppContext.BaseDirectory looking for install.json and falls back to + // the registry's InstallLocation otherwise — on a debug build launched from the repo + // there's no install.json above it, so it returns the *installed* worker's path. That + // never matches the running debug process, so KillMatchingWorkerProcesses silently + // skips it (it only kills a process whose path equals the given exe) and this either + // launches a second worker that immediately dies on the already-bound port, or leaves + // the real one running untouched while nothing visibly changes. + var exe = RunningWorkerExePath() ?? _workerLocator.Find(); if (exe is null) throw new InvalidOperationException("Worker executable not found."); KillMatchingWorkerProcesses(exe); @@ -667,6 +675,20 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable throw new InvalidOperationException($"worker process exited immediately (code {started.ExitCode})"); } + private static string? RunningWorkerExePath() + { + foreach (var p in System.Diagnostics.Process.GetProcessesByName("ClaudeDo.Worker")) + { + try + { + if (p.MainModule?.FileName is { } path) return path; + } + catch { /* inaccessible */ } + finally { p.Dispose(); } + } + return null; + } + // Only kill the worker belonging to THIS installation — not any other // ClaudeDo.Worker on the machine (e.g. a second install). private static void KillMatchingWorkerProcesses(string exe)