fix(herdr): detach daemon bootstrap into own process group via python spawner

- Replace 'nohup ... server & disown' with portable Python 'Popen(..., start_new_session=True)' in lib.sh shim generator
- Isolate headless herdr server in its own SID/PGID to protect against parent process-group signal broadcast (SIGTERM/SIGINT)
- Add regression test test_h26_daemon_spawn_uses_process_group_detachment to verify child PGID detachment (Closes #3 Item 1)
This commit is contained in:
2026-08-31 10:30:42 +09:00
parent 50a9f32773
commit c49ee3bdd8
2 changed files with 61 additions and 3 deletions
+15 -3
View File
@@ -208,9 +208,21 @@ except Exception:
# Headless bootstrap avoids herdr's "nested herdr is disabled" guard that
# blocks a normal interactive `herdr --session <name>` launch from inside
# an existing herdr pane (which is how MAM's own agents usually run).
nohup "$REAL_HERDR" --session "$_MAM_SESSION" server >/dev/null 2>&1 &
_mam_server_pid=$!
disown 2>/dev/null || true
# ISSUE-3: plain `nohup ... & disown` only ignores SIGHUP and drops bash's
# own job-table tracking - it does NOT create a new process group/session,
# so the daemon stays in the caller's PGID and dies when a process-group
# signal (SIGTERM/SIGINT from a test runner, timeout wrapper, or parent
# supervisor teardown) is broadcast to that group. setsid(1) is not
# installed by default on macOS, so spawn via Python's
# start_new_session=True (os.setsid in the child) instead - portable
# across macOS and Linux, and lib_py already depends on Python.
_mam_server_pid=$(python3 -c '
import subprocess, sys
p = subprocess.Popen(sys.argv[1:], start_new_session=True,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
stdin=subprocess.DEVNULL)
print(p.pid)
' "$REAL_HERDR" --session "$_MAM_SESSION" server)
for _mam_wait_i in $(seq 1 40); do
[ -S "${HOME:-$HOME_DIR}/.config/herdr/sessions/$_MAM_SESSION/herdr.sock" ] && break
kill -0 "$_mam_server_pid" 2>/dev/null || break