fix(cli): make grob stop recognise its daemon on macOS - #481
Merged
Conversation
`command_invokes_grob` split the process command line only on NUL. That fits
Linux `/proc/<pid>/cmdline` (`grob\0start\0`) but not macOS `ps -o command=`,
which is space-separated (`/opt/homebrew/bin/grob start`). The whole string
then reached `Path::file_name`, yielding `"grob start"` (≠ `"grob"`), so
`is_process_running` returned false for a live daemon and `grob stop` / the
restart path in `grob start` both refused to act — the daemon could not be
stopped or cleanly restarted on macOS.
Pick the separator by which one is present: NUL when the string contains it
(Linux), whitespace otherwise (macOS). Existing negative cases still hold
(`grob-upgrade`, `python …/grob.py`, `agrob-helper`).
Verified end to end: a daemon started with the fixed binary is now stopped by
`grob stop` ("Service stopped successfully") instead of "refusing to stop".
Note: the pid file is a single global `~/.grob/grob.pid` (one-daemon model), so
with identity now working, `grob start` on any port stops whatever that file
points at. Running a second daemon on another port therefore replaces the first
— expected for the single-daemon design, but worth knowing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Destynova2
enabled auto-merge (squash)
July 26, 2026 09:49
Merged
Destynova2
added a commit
that referenced
this pull request
Jul 27, 2026
## 🤖 New release * `grob`: 0.36.83 -> 0.36.84 <details><summary><i><b>Changelog</b></i></summary><p> <blockquote> ## [0.36.84](v0.36.83...v0.36.84) - 2026-07-27 ### Fixed - *(cli)* make grob stop recognise its daemon on macOS ([#481](#481)) ### Other - switch merge policy to squash-only for reliable release-plz ([#480](#480)) - *(release-plz)* allow manual workflow_dispatch </blockquote> </p></details> --- This PR was generated with [release-plz](https://github.com/release-plz/release-plz/).
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The bug
On macOS,
grob stoprefused to stop its own running daemon:…and
grob start -d(restart) silently left the old daemon running, so arestart never actually took effect.
Cause
command_invokes_grob(the PID-identity guard) split the process command lineonly on NUL. That matches Linux
/proc/<pid>/cmdline(grob\0start\0) but notmacOS
ps -o command=, which is space-separated:With no NUL, the whole string reached
Path::file_name, giving"grob start"(≠
"grob"). Sois_process_runningreturned false for a live daemon, and bothgrob stopand the restart path ingrob startrefused to act.Reproduced:
Fix
Pick the separator by which one is present — NUL when the string contains it
(Linux), whitespace otherwise (macOS):
All existing negative cases still hold (
grob-upgrade,python …/grob.py,agrob-helper).Verification
command_identity_accepts_space_separated_macos_form(the exact
psstring); existing test still green.grob stop→✅ Service stopped successfully(wasrefusing to stop).Note (single-daemon model)
The pid file is a single global
~/.grob/grob.pid. With identity now working,grob starton any port stops whatever that file points at — so a second daemonon another port replaces the first. That's consistent with the one-daemon
design; a port-scoped pid file would be a separate change if parallel daemons
are ever wanted.
🤖 Generated with Claude Code