fix(gateway): restore KeepAlive recovery and skip repair kickstart on running LaunchAgent#78412
fix(gateway): restore KeepAlive recovery and skip repair kickstart on running LaunchAgent#78412wdeveloper16 wants to merge 4 commits into
Conversation
|
Codex review: needs real behavior proof before merge. Summary Reproducibility: yes. for source-level reproduction: current main unconditionally disables launchd on stop and kickstarts after an already-loaded bootstrap. I did not establish a live current-main macOS launchd failure in this read-only review. Real behavior proof Next step before merge Security Review findings
Review detailsBest possible solution: Keep the bootout-by-default and skip-kickstart direction after routing Do we have a high-confidence way to reproduce the issue? Yes for source-level reproduction: current main unconditionally disables launchd on stop and kickstarts after an already-loaded bootstrap. I did not establish a live current-main macOS launchd failure in this read-only review. Is this the best way to solve the issue? No, not as latest head stands. The launchd-side fix is plausible, but Full review comments:
Overall correctness: patch is incorrect Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 79853b2fe8ff. |
fb461b0 to
4cfa185
Compare
4cfa185 to
cb2db08
Compare
|
Thanks for the detailed review. Addressed all findings — summary below. [P2] Docs updated — Changes in this update (commit
Acceptance criteria results (local, head Real macOS launchd proof — I don't have a macOS machine available to attach terminal/log output. This is the outstanding item a maintainer or macOS contributor will need to verify before merge. The unit tests cover the bootout-by-default, any macOS verification or lifecycle-contract feedback would be welcome. |
|
Please don’t spam-ping multiple maintainers at once. Be patient, or join our community Discord for help: https://discord.gg/clawd |
a48dbac to
7fe1c22
Compare
|
Hi, @BunsDev Checks run:
Results:
Live smoke (macOS, launchd): Ran Ran Also confirmed that The installed user Gateway service was restored to running state after smoke testing. Really hope your feedback with this, Thanks. |
b87aece to
65d6caf
Compare
8ef3efa to
8349897
Compare
cfb8b72 to
7e6328a
Compare
|
Hi, @BunsDev |
7e6328a to
2d35973
Compare
2d35973 to
2be5de5
Compare
Summary: - carry forward #78412's macOS LaunchAgent bootout-by-default stop behavior and repair guard - fix the remaining `gateway stop --disable` tail when the service is already not loaded after bootout - add lifecycle regressions, docs, and changelog Verification: - pnpm install - pnpm test src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts src/daemon/launchd.test.ts - pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/cli/daemon-cli/lifecycle-core.ts src/cli/daemon-cli/lifecycle.ts src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts docs/cli/gateway.md docs/gateway/index.md src/daemon/launchd.ts src/daemon/launchd.test.ts src/cli/daemon-cli/register-service-commands.ts src/cli/daemon-cli/types.ts src/daemon/service-types.ts - git diff --check origin/main...HEAD - pnpm build - Parallels macOS Tahoe VM reproduce/fix proof in PR body - PR checks green: Real behavior proof, auto-response, dispatch, label, label-issues Co-authored-by: wdeveloper16 <[email protected]>
|
Thanks for the original fix here. I landed this via maintainer PR #79235 because the branch still had one macOS tail case: after the new default The replacement keeps your bootout-by-default behavior, preserves the running-LaunchAgent repair guard, adds the missing Merged as 1f88cb2 with co-author credit and changelog credit for @wdeveloper16. |
|
Hey, @steipete |
Summary: - carry forward openclaw#78412's macOS LaunchAgent bootout-by-default stop behavior and repair guard - fix the remaining `gateway stop --disable` tail when the service is already not loaded after bootout - add lifecycle regressions, docs, and changelog Verification: - pnpm install - pnpm test src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts src/daemon/launchd.test.ts - pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/cli/daemon-cli/lifecycle-core.ts src/cli/daemon-cli/lifecycle.ts src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts docs/cli/gateway.md docs/gateway/index.md src/daemon/launchd.ts src/daemon/launchd.test.ts src/cli/daemon-cli/register-service-commands.ts src/cli/daemon-cli/types.ts src/daemon/service-types.ts - git diff --check origin/main...HEAD - pnpm build - Parallels macOS Tahoe VM reproduce/fix proof in PR body - PR checks green: Real behavior proof, auto-response, dispatch, label, label-issues Co-authored-by: wdeveloper16 <[email protected]>
Summary: - carry forward openclaw#78412's macOS LaunchAgent bootout-by-default stop behavior and repair guard - fix the remaining `gateway stop --disable` tail when the service is already not loaded after bootout - add lifecycle regressions, docs, and changelog Verification: - pnpm install - pnpm test src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts src/daemon/launchd.test.ts - pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/cli/daemon-cli/lifecycle-core.ts src/cli/daemon-cli/lifecycle.ts src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts docs/cli/gateway.md docs/gateway/index.md src/daemon/launchd.ts src/daemon/launchd.test.ts src/cli/daemon-cli/register-service-commands.ts src/cli/daemon-cli/types.ts src/daemon/service-types.ts - git diff --check origin/main...HEAD - pnpm build - Parallels macOS Tahoe VM reproduce/fix proof in PR body - PR checks green: Real behavior proof, auto-response, dispatch, label, label-issues Co-authored-by: wdeveloper16 <[email protected]>
Summary: - carry forward openclaw#78412's macOS LaunchAgent bootout-by-default stop behavior and repair guard - fix the remaining `gateway stop --disable` tail when the service is already not loaded after bootout - add lifecycle regressions, docs, and changelog Verification: - pnpm install - pnpm test src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts src/daemon/launchd.test.ts - pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/cli/daemon-cli/lifecycle-core.ts src/cli/daemon-cli/lifecycle.ts src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts docs/cli/gateway.md docs/gateway/index.md src/daemon/launchd.ts src/daemon/launchd.test.ts src/cli/daemon-cli/register-service-commands.ts src/cli/daemon-cli/types.ts src/daemon/service-types.ts - git diff --check origin/main...HEAD - pnpm build - Parallels macOS Tahoe VM reproduce/fix proof in PR body - PR checks green: Real behavior proof, auto-response, dispatch, label, label-issues Co-authored-by: wdeveloper16 <[email protected]>
Summary: - carry forward openclaw#78412's macOS LaunchAgent bootout-by-default stop behavior and repair guard - fix the remaining `gateway stop --disable` tail when the service is already not loaded after bootout - add lifecycle regressions, docs, and changelog Verification: - pnpm install - pnpm test src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts src/daemon/launchd.test.ts - pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/cli/daemon-cli/lifecycle-core.ts src/cli/daemon-cli/lifecycle.ts src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts docs/cli/gateway.md docs/gateway/index.md src/daemon/launchd.ts src/daemon/launchd.test.ts src/cli/daemon-cli/register-service-commands.ts src/cli/daemon-cli/types.ts src/daemon/service-types.ts - git diff --check origin/main...HEAD - pnpm build - Parallels macOS Tahoe VM reproduce/fix proof in PR body - PR checks green: Real behavior proof, auto-response, dispatch, label, label-issues Co-authored-by: wdeveloper16 <[email protected]>
Summary: - carry forward openclaw#78412's macOS LaunchAgent bootout-by-default stop behavior and repair guard - fix the remaining `gateway stop --disable` tail when the service is already not loaded after bootout - add lifecycle regressions, docs, and changelog Verification: - pnpm install - pnpm test src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts src/daemon/launchd.test.ts - pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/cli/daemon-cli/lifecycle-core.ts src/cli/daemon-cli/lifecycle.ts src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts docs/cli/gateway.md docs/gateway/index.md src/daemon/launchd.ts src/daemon/launchd.test.ts src/cli/daemon-cli/register-service-commands.ts src/cli/daemon-cli/types.ts src/daemon/service-types.ts - git diff --check origin/main...HEAD - pnpm build - Parallels macOS Tahoe VM reproduce/fix proof in PR body - PR checks green: Real behavior proof, auto-response, dispatch, label, label-issues Co-authored-by: wdeveloper16 <[email protected]>
Summary: - carry forward openclaw#78412's macOS LaunchAgent bootout-by-default stop behavior and repair guard - fix the remaining `gateway stop --disable` tail when the service is already not loaded after bootout - add lifecycle regressions, docs, and changelog Verification: - pnpm install - pnpm test src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts src/daemon/launchd.test.ts - pnpm exec oxfmt --check --threads=1 CHANGELOG.md src/cli/daemon-cli/lifecycle-core.ts src/cli/daemon-cli/lifecycle.ts src/cli/daemon-cli/lifecycle-core.test.ts src/cli/daemon-cli/lifecycle.test.ts docs/cli/gateway.md docs/gateway/index.md src/daemon/launchd.ts src/daemon/launchd.test.ts src/cli/daemon-cli/register-service-commands.ts src/cli/daemon-cli/types.ts src/daemon/service-types.ts - git diff --check origin/main...HEAD - pnpm build - Parallels macOS Tahoe VM reproduce/fix proof in PR body - PR checks green: Real behavior proof, auto-response, dispatch, label, label-issues Co-authored-by: wdeveloper16 <[email protected]>
Summary
src/daemon/launchd.ts. (1)openclaw gateway stopunconditionally calledlaunchctl disablebefore stopping, permanently suppressingKeepAliveauto-recovery — so a later crash or timeout would never respawn the gateway without a manuallaunchctl enable. (2)repairLaunchAgentBootstrapfell through tolaunchctl kickstartunconditionally in thealready-loadedbranch, restarting a healthy running service and disconnecting active WebUI/Discord sessions.gateway stopleft the service disabled — so KeepAlive failed to respawn after a timeout. Issue [Bug]: macOS LaunchAgent repair kickstarts already-loaded running gateway #77428 caused unnecessary gateway restarts and session drops every time the repair path ran against a running gateway.stopLaunchAgentdefault path now callslaunchctl bootoutonly; persistent disable is opt-in via new--disableflag onopenclaw gateway stop.repairLaunchAgentBootstrapcallsreadLaunchAgentRuntimein thealready-loadedbranch and skips kickstart if the service is already running.disable?: booleanadded toGatewayServiceControlArgs, bothDaemonLifecycleOptionstypes, and threaded throughrunServiceStop.--disablebranch are preserved exactly. No behavior change on Linux (systemd) or Windows (schtasks). No gateway protocol or RPC changes.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Real behavior proof
openclaw gateway stopunconditionally calledlaunchctl disable, permanently suppressing KeepAlive so the gateway would not respawn after unexpected crashes. A subsequentopenclaw gateway startalso required a manuallaunchctl enablefirst.src/daemon/launchd.ts:605on2daf3d332ff0(currentmainstill callslaunchctl disableunconditionally beforelaunchctl stop). No live macOS launchd environment available from this contributor.openclaw gateway stop(default path hitslaunchctl bootout);openclaw gateway stop --disable(hitslaunchctl disable+launchctl stop)src/daemon/launchd.test.tsincluding new tests: "stops LaunchAgent via bootout by default", "stops LaunchAgent with disable+stop when --disable is passed", "skips kickstart when already-loaded service is actively running". Live macOS terminal output is not available from this contributor; requestingproof: overridefrom a maintainer.launchctl bootoutreplaceslaunchctl disablein the default stop path (src/daemon/launchd.ts). The--disableflag threads throughGatewayServiceControlArgs→stopLaunchAgentfor the old persistent-disable behavior.kill,--disablesurvival across reboot — no macOS machine available.Root Cause (if applicable)
stopLaunchAgentcalledlaunchctl disableunconditionally (src/daemon/launchd.ts) to prevent launchd from immediately respawning the process afterlaunchctl stop. This is correct for an operator-initiated stop, but the persistent disable state survived subsequent crashes — so KeepAlive could not fire until a manuallaunchctl enablewas run.repairLaunchAgentBootstrapdetected thealready-loadedbootstrap result and fell through tolaunchctl kickstartwithout checking whether the service was already running. Any healthy running gateway that triggered the repair path was unnecessarily restarted.openclaw gateway restartcorrectly callslaunchctl enablebefore bootstrap (vialaunchd-restart-handoff.ts), so restarts recovered cleanly — but a plainstopfollowed by an unexpected crash had no recovery path.Regression Test Plan (if applicable)
src/daemon/launchd.test.tsstopLaunchAgentcallsbootoutonly, neverdisableorlaunchctl stop.stopLaunchAgent({ disable: true })callsdisable + stopwith no bootout on success.repairLaunchAgentBootstrapskips kickstart whenreadLaunchAgentRuntimereportsstatus: "running".repairLaunchAgentBootstrapstill kickstarts when service is stopped or state is unknown.execLaunchctl; unit tests with mockedexecFileUtf8cover the exact conditional branches without requiring a real macOS environment.User-visible / Behavior Changes
openclaw gateway stop(macOS): default behavior changed — no longer callslaunchctl disable; useslaunchctl bootoutinstead. KeepAlive auto-recovery survives future crashes after a stop.openclaw gateway stop --disable(macOS): new flag — opts into the old persistent-disable behavior. Use when you explicitly want the gateway to stay down across crashes until nextgateway start.openclaw gateway stopon Linux and Windows: no change —--disableflag is accepted by the CLI but ignored by systemd and schtasks stop implementations.repairLaunchAgentBootstrap: no longer restarts a running gateway. Behavior is unchanged when the service is stopped or state is unknown.Diagram (if applicable)
Security Impact (required)
launchctl bootoutreplaceslaunchctl disable + stopin the default path; both are local macOS system calls with identical privilege requirements.Repro + Verification
Environment
gui/$UID/ai.openclaw.gateway)KeepAlive: true,RunAtLoad: truein generated plistSteps
openclaw statusopenclaw gateway stoplaunchctl print gui/$UID/ai.openclaw.gateway→ should return "Could not find service" (not disabled)openclaw gateway start→ gateway startskill $(pgrep -f "openclaw gateway")openclaw gateway stop --disable→ verify service is disabled; KeepAlive does not respawnExpected
openclaw gateway startActual (before fix)
launchctl enable + bootstraprequiredEvidence
Human Verification (required)
bootoutconfirmed via test mock assertions onlaunchctlCalls;--disablepath preserves full disable+stop+fallback chain; repair skips kickstart on running service; repair still kickstarts on stopped service; all failure/fallback paths for both fixes tested.isLaunchctlNotLoaded);readLaunchAgentRuntimereturns"unknown"(falls through to kickstart — safe conservative default);--disableon Linux/Windows (flag accepted, ignored by systemd/schtasks stop implementations).launchctlcalls (Crabbox unavailable). Integration e2e test atlaunchd.integration.e2e.test.tscovers the stop path structurally but does not run against a real launchd domain in CI.Review Conversations
Compatibility / Migration
gateway stopbehavior changes, but the new behavior is strictly safer. No config or env changes needed.gateway stopto permanently suppress KeepAlive should add--disableto their scripts or automations.Risks and Mitigations
bootoutdoes not prevent launchd from reloading the plist on next login (KeepAlive: true+RunAtLoad: trueare in the plist). A user who stops the gateway and reboots may see it auto-start.KeepAlive: true. Operators who want permanent suppression across reboots should use--disableoropenclaw gateway uninstall. Documented in the--disableflag description.readLaunchAgentRuntimeadds onelaunchctl printcall in thealready-loadedrepair branch.already-loadedbranch is only hit whenlaunchctl bootstrapreturns exit 130 or "already exists in domain" — an edge case on repair paths, not the hot path. Single synchronous shell call; negligible latency.