Skip to content

mcpx daemons: dead records listed forever, custom-state daemons missed, paths.config file entry silently ignored #278

Closed

Description

@dezren39

Summary

mcpx daemons promises "every daemon on this machine" (internal/cli/manual.go), but daemon discovery in this repo's pkgs/mcpx copy has three defects. All three were found and fixed in dezren39/darwin-nix#369 (PR dezren39/darwin-nix#381); the fix was never ported to this copy. The pre-fix files here are byte-identical to dezren39/nix's pre-fix versions (verified: internal/daemon/paths.go, paths_test.go, server.go, internal/cli/manual.go, internal/settings/consumed_test.go, docs/parity.md; commands.go/root.go differ only by this repo's already-ported #265/#274/#276 fixes, in unrelated hunks).

Defects (verified on main 584d869)

  1. Dead daemons are listed forever. Paths.ListDaemons (internal/daemon/paths.go:229) returns every daemon*.json record in the state dir with no liveness check, and nothing ever removes a record whose daemon was killed or crashed (a clean exit removes its own record at server.go:488/1158; a killed one cannot). Reproduced with a binary built from main: a record with a dead PID is listed by mcpx daemons as RUNNING false, and the record file is still on disk after repeated listings. stop --all iterates the same stale records.

  2. Daemons with their own state directory are invisible. A daemon started with a custom state dir (paths.state in config, --paths-state, or MCPX_STATE_DIR) writes its record only there (WriteInfo, paths.go:284). ListDaemons scans only the caller's own state dir, so a normal mcpx daemons / stop --all never sees it. Reproduced: a live record placed only in a second state dir is listed from that dir but not from the first.

  3. A paths.config entry naming a file is silently ignored. The entries are directories searched for config files; pointing one at a file (a natural reading of the setting) silently loads the default config instead. adoptSettings (internal/cli/root.go:195) never inspects the entries, and internal/settings/consumed_test.go keeps paths.config on the unread-allowlist as "circular by construction".

Suggested fix (mirror dezren39/nix PR #381)

  • Paths.PruneStale(): remove records whose PID is gone (signal-0 liveness, EPERM = alive; pid-less records kept); mcpx daemons and stop --all prune before listing.
  • Mirror records: WriteInfo also publishes the record into the default state dir (keyed name, best-effort) when the daemon's state dir is custom; new RemoveInfo() removes primary + mirror and replaces the two os.Remove(s.paths.Info) calls in server.go. Listings search the caller's dir + the default dir, deduped by socket.
  • adoptSettings warns once when a paths.config entry names a file, naming --config/MCPX_CONFIG as the way to pick a file; paths.config leaves the consumed-test allowlist.
  • Update the daemons entry in internal/cli/manual.go and regenerate docs/parity.md.

Acceptance criteria

  • A daemon record with a dead PID no longer appears in mcpx daemons output and its file is removed; records with a live PID or no PID are untouched.
  • A daemon started in a custom state dir appears in a default-state-dir mcpx daemons listing (and stop --all reaches it), exactly once.
  • A paths.config entry naming a file produces one stderr warning naming --config/MCPX_CONFIG; directory entries stay silent.
  • New daemon tests cover ProcessAlive/PruneStale/mirroring; go test ./internal/daemon/ ./internal/settings/ ./internal/cli/ passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions