diff --git a/src/main/daemon/daemon-init.test.ts b/src/main/daemon/daemon-init.test.ts index 6557ea54478..768a9668844 100644 --- a/src/main/daemon/daemon-init.test.ts +++ b/src/main/daemon/daemon-init.test.ts @@ -807,11 +807,13 @@ describe('daemon-init: runRestartDaemon (7-step sequence)', () => { await replacementAdapter.options.respawn?.('daemon_died') expect(originalSpawner.resetHandle).toHaveBeenCalledTimes(1) expect(originalSpawner.ensureRunning).toHaveBeenCalledTimes(1) - // STA-2376: an observed daemon death → respawn emits the retirement lifecycle event. + // STA-2376: death → respawn retires; runtime unhealthy_resolver is a replace (not a false death). expect(trackDaemonRetiredMock).toHaveBeenCalledWith('died_respawn') trackDaemonRetiredMock.mockClear() + trackDaemonReplacedMock.mockClear() await replacementAdapter.options.respawn?.('unhealthy_resolver') expect(trackDaemonRetiredMock).not.toHaveBeenCalled() + expect(trackDaemonReplacedMock).toHaveBeenCalledWith('unhealthy_resolver', 0) // Still only one spawner in the whole test — nobody new was constructed. expect(spawnerInstances).toHaveLength(1) }) diff --git a/src/main/daemon/daemon-init.ts b/src/main/daemon/daemon-init.ts index 0e578492fae..245d87a5dcf 100644 --- a/src/main/daemon/daemon-init.ts +++ b/src/main/daemon/daemon-init.ts @@ -702,6 +702,9 @@ export async function initDaemonPtyProvider( if (reason === 'daemon_died') { console.warn('[daemon] Daemon process died — respawning') trackDaemonRetired('died_respawn') + } else if (reason === 'unhealthy_resolver') { + // Runtime resolver replace (adapter only fires when live sessions are verified empty). Not a death. + trackDaemonReplaced('unhealthy_resolver', 0) } newSpawner.resetHandle() await newSpawner.ensureRunning() @@ -887,6 +890,9 @@ async function runRestartDaemon(): Promise { if (reason === 'daemon_died') { console.warn('[daemon] Daemon process died — respawning') trackDaemonRetired('died_respawn') + } else if (reason === 'unhealthy_resolver') { + // Runtime resolver replace (adapter only fires when live sessions are verified empty). Not a death. + trackDaemonReplaced('unhealthy_resolver', 0) } currentSpawner.resetHandle() await currentSpawner.ensureRunning() diff --git a/src/main/daemon/daemon-lifecycle-event.ts b/src/main/daemon/daemon-lifecycle-event.ts index 74d27e72a7e..437844ee5ca 100644 --- a/src/main/daemon/daemon-lifecycle-event.ts +++ b/src/main/daemon/daemon-lifecycle-event.ts @@ -9,8 +9,8 @@ import { } from '../../shared/daemon-lifecycle-telemetry' import { track } from '../telemetry/client' -// Startup launcher replaced a still-connectable daemon. `versionSkew` = daemon's pid-file appVersion -// differs from the current app (omit when the decision didn't hinge on version, e.g. a different app path). +// Replaced a still-connectable daemon (startup launcher or runtime resolver-health path). +// `versionSkew` = pid-file appVersion differs from current app (omit when not version-driven). export function trackDaemonReplaced( reason: DaemonReplaceReason, liveSessionCount: number | null,