fix(maestro): installer unit sets KillMode=process so restarts keep sessions (NAP-330)
ci / rust (push) Successful in 5m47s

The generated `maestro.service` used systemd's default
`KillMode=control-group`, so `systemctl restart` (any update/auto-update)
SIGKILLed the daemon's whole cgroup — including the tmux server and every
`maestro-<uuid>` session. recover() then found nothing, silently destroying
all live sessions and their processes on every restart.

Emit `KillMode=process` in the daemon unit so only the daemon's MainPID is
signalled; the tmux server survives and recover() re-adopts the sessions on
the next start. (The web unit spawns no tmux, so it's left unchanged.)

Tests assert the daemon unit carries KillMode=process and the web unit does
not. INSTALL.md notes that updating keeps sessions, and that units written
before this default need a refresh.

Verified live on a host: with KillMode=process a restart leaves the session's
PID unchanged (recovered=1); without it, recovered=0 and the process is gone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-16 09:19:12 +00:00
co-authored by Claude Opus 4.8
parent ba73a713db
commit 4247b5706e
2 changed files with 31 additions and 1 deletions
+27
View File
@@ -58,6 +58,11 @@ pub fn render_user_unit(exec_start: &Path) -> String {
Type=simple\n\
ExecStart={exec} daemon\n\
ExecReload=/bin/kill -HUP $MAINPID\n\
# Signal only the daemon on stop/restart, not its whole cgroup —\n\
# otherwise systemd kills the tmux server (and every session) the\n\
# daemon spawned, defeating tmux persistence. recover() re-adopts the\n\
# surviving sessions on the next start (NAP-330).\n\
KillMode=process\n\
Restart=on-failure\n\
RestartSec=5\n\
\n\
@@ -137,6 +142,28 @@ mod tests {
assert!(body.contains("Restart=on-failure"));
}
#[test]
fn unit_sets_killmode_process_so_restart_keeps_sessions() {
// NAP-330: without KillMode=process, `systemctl restart` SIGKILLs the
// daemon's whole cgroup — including the tmux server and every session.
let body = render_user_unit(Path::new("/x/maestro"));
assert!(
body.contains("\nKillMode=process\n"),
"daemon unit must set KillMode=process; got:\n{body}"
);
}
#[test]
fn web_unit_does_not_set_killmode() {
// The web unit spawns no tmux, so it has no cgroup-kill problem and
// shouldn't carry the override.
let body = render_web_unit(Path::new("/x/maestro-web"));
assert!(
!body.contains("KillMode"),
"web unit needs no KillMode; got:\n{body}"
);
}
#[test]
fn unit_wires_execreload_to_sighup() {
// NAP-237: `systemctl --user reload maestro` must trigger our