Skip to content

🐛 v3.4.4 Fix daemon env for plugins sync#241

Merged
bmeares merged 4 commits into
mainfrom
dev
Jun 23, 2026
Merged

🐛 v3.4.4 Fix daemon env for plugins sync#241
bmeares merged 4 commits into
mainfrom
dev

Conversation

@bmeares

@bmeares bmeares commented Jun 23, 2026

Copy link
Copy Markdown
Owner

No description provided.

@bmeares bmeares self-assigned this Jun 23, 2026
…inks

Two daemon/plugin robustness fixes (replaces the earlier "sync symlinks on daemon
fork" attempt, which had no effect — the wipe happened after it).

1. Stop detached daemons that lost their PID file (utils/daemon/Daemon.py).
   A daemon is launched via `venv_exec` with `Daemon(daemon_id='<id>')` embedded in
   the executed code, so the daemon_id stays visible in the process command line. When
   the PID file is lost (the launcher exited without cleanup, leaving an orphaned
   daemon), `Daemon.pid` now recovers the PID by scanning for that marker, so status
   reflects reality instead of a false "stopped". `kill()`/`quit()` reap EVERY process
   matching the daemon_id (SIGTERM then SIGKILL) — no more `pkill meerschaum` or hunting
   the id in htop. New helpers: `_find_detached_pids`, `_kill_detached_processes`.

2. Don't remove valid plugin symlinks during sync (plugins/__init__.py).
   `sync_plugins_symlinks()` removed any symlink whose target wasn't in the CURRENT
   `PLUGINS_DIR_PATHS`. That path list is process-global and transiently falls back to
   the host default when `MRSM_PLUGINS_DIR` is momentarily absent (observed across
   daemon threads / replace_env contexts), so a sync in that window deleted another
   root's project plugin symlinks from the shared per-root `.internal/plugins` — making
   a `plugin:`-backed job fail to import. Now only TRULY stale symlinks (target gone)
   are reaped; valid cross-dir symlinks are left in place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bmeares and others added 2 commits June 23, 2026 13:55
`replace_env`'s finally cleared `os.environ` entirely (`clear()` +
`update(old_environ)`) before restoring it. In a multi-threaded daemon
(job target + check-jobs RepeatTimer) a concurrent `sync_plugins_symlinks`
could read `MRSM_PLUGINS_DIR` as absent during that blank window, fall back
to the host plugins dir, and never create a project's plugin symlinks — so
a `plugin:<name>` background job failed to import its plugin.

- Restore `os.environ` by diffing instead of clearing it: keys present in
  `old_environ` are never removed, so `MRSM_PLUGINS_DIR` never momentarily
  vanishes.
- Serialize the process-global env/paths swap under a new `_replace_env_lock`
  (held only around the mutation, not across `yield`).

Complements the dev1 only-reap-stale symlink fix (which stopped the wipe
class); this stops the never-created class.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`unload_plugins(remove_symlinks=True)` deleted a plugin's symlink from the
per-root `.internal/plugins` directory unconditionally. That directory is
SHARED by every process operating on the root, including a `plugin:<name>`
background job started moments earlier. `mrsm compose start jobs` loads the
project plugins, starts the job daemon (which needs the symlink), then unloads
the plugins it loaded — and the unload removed the symlink out from under the
running daemon, so its plugin import failed with "Plugin '<name>' cannot be
found". This was the never-created/never-persisted symlink class behind the
`compose start jobs` failures (`compose up` happened to dodge it).

Apply the same only-reap-stale invariant already used in
`sync_plugins_symlinks`: only remove a symlink whose target no longer exists.
In-memory unloading (popping `sys.modules`) is process-local and is all that
unloading needs; symlink lifecycle is owned by `sync_plugins_symlinks`.

Verified against a throwaway compose project with a `plugin:`-backed job:
`compose start jobs` (in-process AND isolation: subprocess), stop+start
persistence, and `compose up` all keep the symlink and import the plugin;
genuinely stale symlinks (target gone) are still cleaned.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bmeares bmeares changed the title 🐛 3.4.4 Sync plugins symlinks on daemon fork 🐛 v3.4.4 Fix daemon env for plugins sync Jun 23, 2026
@bmeares bmeares marked this pull request as ready for review June 23, 2026 18:23
@bmeares bmeares merged commit f56fbe6 into main Jun 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant