You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
treat planned gateway --replace takeovers as restart stops so restart lifecycle markers/notifications are preserved
recover hermes update on macOS when the LaunchAgent plist exists but the launchd job is currently unloaded
add regression coverage for the installed-but-unloaded launchd update path
Why
A macOS gateway update can leave the LaunchAgent plist installed while launchctl list <label> reports the job as unloaded. Before this change, hermes update skipped the gateway restart path in that state, leaving Telegram/cron/gateway delivery offline until the user manually ran hermes gateway start.
Planned --replace gateway takeovers are operational restarts from the user's perspective, so they should use the restart stop path to preserve the expected lifecycle marker and notification semantics.
Verification comment from code review — this PR is clean.
The two-part fix is well-scoped:
gateway/run.py: passing restart=planned_takeover to runner.stop() preserves the "restarting" / "back online" lifecycle semantics during --replace takeover.
hermes_cli/main.py: when launchctl list returns non-zero (job installed but unloaded), calling launchd_start() instead of launchd_restart() recovers the edge case where a previous stop/update left the LaunchAgent dormant.
The test test_gateway_update_starts_installed_but_unloaded_launchd_job directly exercises the new code path with a returncode=3 mock (matching macOS's "not loaded" exit code). Clean, focused, no behavioral side effects on the happy path.
Thanks for the focused fix. Current main still has both target gaps: gateway/run.py:20688 calls runner.stop() without restart state after consuming a planned takeover marker, while hermes_cli/main.py:10858-10873 only restarts launchd jobs whose launchctl list probe succeeds. The proposed launchd_start() route matches the existing unloaded-job recovery in hermes_cli/gateway.py:4206-4233, and GatewayRunner.stop(restart=True) activates the restart-marker path at gateway/run.py:8398-8410.
The surrounding code has moved since the PR was opened, but the targeted expressions remain materially unchanged, so salvage should be mechanical with line-offset/context resolution.
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
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
--replacetakeovers as restart stops so restart lifecycle markers/notifications are preservedhermes updateon macOS when the LaunchAgent plist exists but the launchd job is currently unloadedWhy
A macOS gateway update can leave the LaunchAgent plist installed while
launchctl list <label>reports the job as unloaded. Before this change,hermes updateskipped the gateway restart path in that state, leaving Telegram/cron/gateway delivery offline until the user manually ranhermes gateway start.Planned
--replacegateway takeovers are operational restarts from the user's perspective, so they should use the restart stop path to preserve the expected lifecycle marker and notification semantics.Fixes #42006.
Related to #37388 and #42524.
Test plan
Result:
3 passed in 9.13s