fix(issue-45): MAM herdr servers start with restore-on-restart off; resume closes its own restored empty shell (v4.8.5) #48

Merged
Godopu merged 14 commits from fix/issue-45 into main 2026-10-05 09:56:40 +00:00
3 changed files with 41 additions and 1 deletions
Showing only changes of commit ed73f67a4f - Show all commits

No files matched your search

+15 -1
View File
@@ -230,6 +230,18 @@ except Exception:
# identity to its sub-agents. Drop only per-session runtime markers - user
# configuration such as CLAUDE_PROJECT_DIR or CLAUDE_CONFIG_DIR still
# inherits. The family ids mirror what orc_onboard.sh auto-detects.
#
# ISSUE-45: the server restores its saved layout on start and would
# relaunch every saved agent with herdr's own resume command (no permission
# flags, deleted agents included). It gets HERDR_CONFIG_PATH = the user's
# herdr config with [session] resume_agents_on_restore = false, rebuilt on
# every start (lib_py/herdr_config.py). Without that file the server still
# starts - with restore on, as before.
MAM_SERVER_CFG=$(python3 -m lib_py.herdr_config server-config --fallback-dir "$_SHIM_DIR/.." || true)
export MAM_SERVER_CFG
if [ -z "$MAM_SERVER_CFG" ]; then
echo "WARNING: restore-on-restart stays enabled for herdr session $_MAM_SESSION (no server config)" >&2
fi
_mam_server_pid=$(python3 -c '
import os, subprocess, sys
deny = ("CLAUDECODE", "CLAUDE_CODE_CHILD_SESSION", "CLAUDE_CODE_SESSION_ID",
@@ -237,8 +249,10 @@ deny = ("CLAUDECODE", "CLAUDE_CODE_CHILD_SESSION", "CLAUDE_CODE_SESSION_ID",
"CLAUDE_CODE_MESSAGING_TOKEN", "CLAUDE_CODE_MESSAGING_SOCKET",
"CLAUDE_CODE_BRIDGE_SESSION_ID",
"ANTIGRAVITY_CONVERSATION_ID", "HERMES_SESSION_ID", "GROK_SESSION_ID",
"OPENCODE_SESSION_ID")
"OPENCODE_SESSION_ID", "MAM_SERVER_CFG")
env = {k: v for k, v in os.environ.items() if k not in deny}
if os.environ.get("MAM_SERVER_CFG"):
env["HERDR_CONFIG_PATH"] = os.environ["MAM_SERVER_CFG"]
p = subprocess.Popen(sys.argv[1:], start_new_session=True, env=env,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
stdin=subprocess.DEVNULL)
+5
View File
@@ -236,6 +236,11 @@ def mam_sandbox(tmp_path, monkeypatch, request):
monkeypatch.delenv("HERDR_SESSION_NAME", raising=False)
monkeypatch.delenv("HERDR_SERVER_NAME", raising=False)
monkeypatch.delenv("HERDR_WORKSPACE_ID", raising=False)
# Issue #45: the shim bootstrap writes a herdr server config next to the
# user's; with these unset that is $HOME/.config/herdr, inside the sandbox.
# Every pane of a MAM server inherits HERDR_CONFIG_PATH.
monkeypatch.delenv("HERDR_CONFIG_PATH", raising=False)
monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
import sys
monkeypatch.setenv("AGENT_PYTHON_BIN", sys.executable)
+21
View File
@@ -150,6 +150,27 @@ def test_bootstrap_strips_agent_runtime_markers(mam_sandbox):
assert key in server_env
def test_bootstrap_hands_the_server_a_no_restore_config(mam_sandbox):
"""Issue #45: the server starts with HERDR_CONFIG_PATH = the user's herdr
config plus [session] resume_agents_on_restore = false (that herdr obeys it
is test_issue45_restore.py, on a real server)."""
import tomllib
env = _install_fake_herdr(mam_sandbox)
user_cfg = mam_sandbox / ".config" / "herdr" / "config.toml"
user_cfg.parent.mkdir(parents=True)
user_cfg.write_text("[ui]\nconfirm_close = false\n")
res = _run_through_shim(mam_sandbox, env, "issue45-probe")
assert res.returncode == 0, res.stderr
assert "restore-on-restart stays enabled" not in res.stderr, res.stderr
server_env = json.loads(Path(env["FAKE_HERDR_ENV_DUMP"]).read_text())
assert server_env.get("HERDR_CONFIG_PATH") == str(user_cfg.parent / "mam-server-config.toml")
assert "MAM_SERVER_CFG" not in server_env
cfg = tomllib.loads(Path(server_env["HERDR_CONFIG_PATH"]).read_text())
assert cfg == {"ui": {"confirm_close": False}, "session": {"resume_agents_on_restore": False}}
def test_no_autostart_skips_bootstrap_but_still_routes_the_call(mam_sandbox):
env = _install_fake_herdr(mam_sandbox)
env["MAM_NO_AUTOSTART"] = "1"