fix(desktop): reap stale gateway orphans on Desktop backend startup - #78002
fix(desktop): reap stale gateway orphans on Desktop backend startup#78002JoaoMarcos44 wants to merge 1 commit into
Conversation
Desktop tears down the previous backend with a plain SIGTERM on POSIX or a full process-tree kill on Windows (backend-child.ts). Neither path reaches a gateway spawned via `hermes gateway restart`, which detaches into its own session (start_new_session=True). On hosts without a service supervisor this stacked a duplicate, unsupervised gateway on every Desktop restart, splitting/duplicating inbound messages on connected platforms. _reap_unsupervised_gateway_orphans already solves this exact class of bug for the CLI restart path (NousResearch#51325, NousResearch#75936), including the systemd-aware no-op gate. Reuses it on Desktop backend startup instead of adding new kill logic or terminating _ACTION_PROCS generically (which would have killed legitimate one-shot actions like backup and hermes-update that are meant to survive a backend restart). Fixes NousResearch#77276
Related: #77721 also fixes #77276 using |
|
Resolved on main — and a credit correction is owed here: you submitted this startup-path reap on Aug 3, a day before #78312 (merged via #83406), making you the earliest submitter of this fix. Our duplicate sweep missed this PR at salvage time; apologies for that. Noting for the record that the design (reuse The merged implementation is functionally equivalent to yours; #83542 additionally covered the restart path and shutdown termination. Thank you for the contribution and the clear analysis of the detached-session gap in your PR body. |
Fixes #77276
Summary
Desktop restarts can leave a gateway process orphaned on hosts without a service supervisor (WSL, no-systemd Linux, some Windows setups), causing a duplicate gateway to run alongside the freshly-started one — which splits or duplicates inbound messages on connected platforms (QQ, Discord, etc.).
Root cause
Desktop tears down the previous backend two ways, neither of which reaches a detached gateway:
SIGTERMto the backend's direct child only (apps/desktop/electron/backend-child.ts) — deliberately, since the backend itself isn't spawned detached and there's no process group to negative-PID-kill.taskkill /T /F).But the actual gateway daemon is spawned via
hermes gateway restart, which detaches into its own session (start_new_session=True/ Windows detach flags) specifically so it does survive backend churn intentionally for one-shot maintenance actions (backup,hermes-update,doctor, etc.). That same detachment means a plain SIGTERM never reaches it — and on hosts without a supervisor watching the gateway's lifecycle, the old instance just keeps running after the new backend starts and spawns its own.I first considered patching
_lifespan's shutdown toterminate()everything tracked in_ACTION_PROCS. Rejected: that dict also tracks legitimate one-shot actions that are supposed to outlive a backend restart (backup,hermes-update,doctor,skills-install...) — a blanket kill would corrupt in-flight backups/updates, trading one bug for a worse one.Fix
hermes_cli/gateway.pyalready has_reap_unsupervised_gateway_orphans()— built and tested for the exact same failure mode on the CLI restart path (#51325, #75936), including thesupports_systemd_services()gate so it's a no-op wherever a real supervisor already owns the gateway's lifecycle. This PR calls it once, on Desktop backend startup, before the new backend can spawn its own gateway — closing the Desktop-restart gap with already-proven logic instead of inventing new kill paths.Test plan
test_startup_reaps_gateway_orphans_when_desktop— sweep runs whenHERMES_DESKTOP=1test_startup_skips_gateway_reap_when_not_desktop— server-mode backend does not run the desktop-only sweeptests/hermes_cli/test_web_server.pyfull suite: 134 passed, 5 skippedtests/hermes_cli/test_gateway.pyfull suite: 10 passed, 5 skipped (no regression on the reused reaper)%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#8b0000', 'mainBkg': '#0a0204', 'primaryTextColor': '#ffccd5', 'primaryBorderColor': '#ff0038', 'lineColor': '#ff0038'}}}%% graph TD A[🩸 Desktop Restart] -->|POSIX SIGTERM / Win taskkill /T| B[⚰️ Old Backend Dies] C[🔥 Detached Gateway Session] -->|start_new_session=True| D[👻 Survives As Orphan] B --> D D --> E{New Backend Boots} E -->|HERMES_DESKTOP=1| F[⚔️ _reap_stale_desktop_gateway_orphans] F -->|Reuses #51325/#75936 Reaper| G[🕯️ Orphan Exorcised Before Fresh Gateway Spawns]Infographic :