fix(gateway): wait for launchd restart readiness in-band (#56524) - #56908
fix(gateway): wait for launchd restart readiness in-band (#56524)#56908izumi0uu wants to merge 1 commit into
Conversation
…h#56524) Constraint: launchd in-band restarts can kill the foreground tool before a fallback kickstart runs\nRejected: return success immediately after SIGUSR1 | launchd may not have spawned a runtime-ready replacement yet\nConfidence: high\nScope-risk: moderate\nDirective: keep launchd self-restart, watcher fallback, and readiness waits on one shared recovery path\nTested: ./scripts/run_tests.sh tests/hermes_cli/test_gateway_service.py -k 'launchd_restart_drains_running_gateway_before_kickstart or launchd_restart_self_request or wait_for_launchd_service_restart_timeout_reports_logs or launchd_restart_boots_out_stale_registration_before_bootstrap or launchd_restart_falls_back_to_detached_on_error_5'\nTested: .venv/bin/python -m pytest -q tests/hermes_cli/test_gateway_service.py -k 'launchd_restart_self_request or wait_for_launchd_service_restart_timeout_reports_logs'\nTested: ruff check hermes_cli/gateway.py tests/hermes_cli/test_gateway_service.py\nTested: git diff --check\nNot-tested: full repo pytest remains blocked by six pre-existing user-systemd failures in tests/hermes_cli/test_gateway_service.py on this macOS host
Fix PR for #56524, in the macOS launchd gateway-respawn family. Distinct mechanism from the sibling launchd fixes -- #42450 (drops |
|
Thanks for the focused launchd recovery work. The premise is confirmed on current main: the in-band branch reports success immediately after SIGUSR1 at The proposed readiness predicate matches the established systemd contract in Automated hermes-sweeper review. |
What does this PR do?
Fixes the macOS launchd in-band restart/update path so Hermes does not report success before a fresh gateway process is actually runtime-ready.
The old self-restart path returned immediately after sending
SIGUSR1, which lethermes update/hermes gateway restartcomplete while the old gateway was still draining and before launchd had confirmed a healthy replacement. In the gateway-hosted terminal path, that also left fallbacklaunchctl kickstart -kvulnerable to being killed with the old gateway process group.This PR waits for the old PID to exit, waits for a fresh launchd-managed PID to reach
gateway_state=running, and uses a detached watcher plus a shared launchd recovery helper so forced fallback survives in-band teardown.Related Issue
Fixes #56524
Type of Change
Changes Made
hermes_cli/gateway.py_wait_for_pid_exit()and_wait_for_launchd_service_restart()so launchd restart success means a fresh runtime-ready gateway PID, not just a signal requestkickstart/ unloaded-job recovery / detached fallback behind_kickstart_launchd_service_and_wait()launchd_restart()through the shared recovery path and only clear the unsupported marker after runtime-ready recoverytests/hermes_cli/test_gateway_service.pylaunchctl kickstart -kNonePID gapbootout -> bootstrap -> kickstartHow to Test
#56524:✓ Service restart requestedimmediately afterSIGUSR1, before launchd runtime-ready confirmation./scripts/run_tests.sh tests/hermes_cli/test_gateway_service.py -k 'launchd_restart_drains_running_gateway_before_kickstart or launchd_restart_self_request or wait_for_launchd_service_restart_timeout_reports_logs or launchd_restart_boots_out_stale_registration_before_bootstrap or launchd_restart_falls_back_to_detached_on_error_5'.venv/bin/python -m pytest -q tests/hermes_cli/test_gateway_service.py -k 'launchd_restart_self_request or wait_for_launchd_service_restart_timeout_reports_logs'ruff check hermes_cli/gateway.py tests/hermes_cli/test_gateway_service.pygit diff --check.venv/bin/python -m pytest -q tests/hermes_cli/test_gateway_service.py182 passed, 6 failed; the 6 failures are the pre-existing user-systemd baseline failures, unrelated to this patchChecklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs
N/A