feat(create,resume,stop): verify and standardize --herdr-session option with full peer review
- Standardize --herdr-session as primary flag with --herdr-server alias across create_session.sh, resume_session.sh, update_yaml_resumed.sh, and stop_session.sh - Guard HERDR_SESSION_NAME in create_session.sh from being overwritten by workspace slug defaults when explicitly provided - Forward explicit --herdr-session from resume_session.sh to update_yaml_resumed.sh and force-update row metadata - Add 5 new Tier 2 component tests covering CLI dry-run parsing, usage matching, default preservation, YAML serialization, and resume propagation - Update multi-agent-mux-create/SKILL.md documentation - Verified by autonomous multi-agent loop with unanimous PASS verdicts from Claude and Cline
This commit is contained in:
@@ -30,7 +30,7 @@ def get_mqtt_common(mam_sandbox):
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# FEATURE 1: Create Session (5 Test Cases)
|
||||
# FEATURE 1: Create Session (9 Test Cases)
|
||||
# ==============================================================================
|
||||
|
||||
def test_comp_create_schema_validation(mam_sandbox):
|
||||
@@ -133,8 +133,122 @@ def test_comp_create_sqlite_tables_created(mam_sandbox, mock_herdr, mock_agents)
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_comp_create_usage_matches_parser(mam_sandbox, mock_herdr, mock_agents):
|
||||
"""Verify that create_session.sh usage documents --herdr-session and parser accepts it."""
|
||||
script = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-create" / "scripts" / "create_session.sh"
|
||||
res = subprocess.run(["bash", str(script), "--help"], capture_output=True, text=True)
|
||||
assert res.returncode == 0
|
||||
assert "--herdr-session" in res.stdout
|
||||
assert "--herdr-server" in res.stdout
|
||||
for agent in ("claude", "agy", "hermes", "cline"):
|
||||
assert agent in res.stdout
|
||||
|
||||
# Test parser acceptance of valid flags vs unknown arg rejection
|
||||
r1 = subprocess.run([
|
||||
"bash", str(script),
|
||||
"--workspace", str(mam_sandbox),
|
||||
"--agent", "claude",
|
||||
"--role", "Creator",
|
||||
"--herdr-session", "test-sess",
|
||||
"--dry-run"
|
||||
], capture_output=True, text=True)
|
||||
assert r1.returncode == 0
|
||||
assert "unknown arg" not in r1.stderr
|
||||
|
||||
r2 = subprocess.run([
|
||||
"bash", str(script),
|
||||
"--invalid-flag-xyz"
|
||||
], capture_output=True, text=True)
|
||||
assert r2.returncode == 2
|
||||
assert "unknown arg" in r2.stderr
|
||||
|
||||
|
||||
def test_comp_create_herdr_session_cli_parsing_dry_run(mam_sandbox, mock_herdr, mock_agents):
|
||||
"""Verify that --herdr-session and --herdr-server are parsed cleanly in --dry-run mode."""
|
||||
script = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-create" / "scripts" / "create_session.sh"
|
||||
res1 = subprocess.run([
|
||||
"bash", str(script),
|
||||
"--workspace", str(mam_sandbox),
|
||||
"--agent", "claude",
|
||||
"--role", "Creator",
|
||||
"--herdr-session", "test-isolated-sess",
|
||||
"--dry-run"
|
||||
], capture_output=True, text=True)
|
||||
assert res1.returncode == 0
|
||||
assert "[dry-run] would spawn:" in res1.stdout
|
||||
assert "herdr_session=test-isolated-sess" in res1.stdout
|
||||
|
||||
res2 = subprocess.run([
|
||||
"bash", str(script),
|
||||
"--workspace", str(mam_sandbox),
|
||||
"--agent", "claude",
|
||||
"--role", "Creator",
|
||||
"--herdr-server", "test-isolated-srv",
|
||||
"--dry-run"
|
||||
], capture_output=True, text=True)
|
||||
assert res2.returncode == 0
|
||||
assert "[dry-run] would spawn:" in res2.stdout
|
||||
assert "herdr_session=test-isolated-srv" in res2.stdout
|
||||
|
||||
|
||||
def test_comp_create_herdr_session_default_preserved(mam_sandbox, mock_herdr, mock_agents):
|
||||
"""Verify --herdr-session default is honored and not overwritten by workspace slug."""
|
||||
script = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-create" / "scripts" / "create_session.sh"
|
||||
res = subprocess.run([
|
||||
"bash", str(script),
|
||||
"--workspace", str(mam_sandbox),
|
||||
"--agent", "claude",
|
||||
"--role", "Creator",
|
||||
"--session", "custom-proj-default-claude",
|
||||
"--herdr-session", "default"
|
||||
], capture_output=True, text=True)
|
||||
assert res.returncode == 0, res.stderr
|
||||
|
||||
yaml_path = mam_sandbox / ".mam" / "agent-sessions.yaml"
|
||||
import yaml
|
||||
with open(yaml_path) as f:
|
||||
data = yaml.safe_load(f)
|
||||
sessions = data.get("herdr_sessions", [])
|
||||
assert len(sessions) == 1
|
||||
s = sessions[0]
|
||||
assert s["name"] == "custom-proj-default-claude"
|
||||
assert s["herdr_session"] == "default"
|
||||
assert s["herdr_server"] == "default"
|
||||
assert "HERDR_SESSION_NAME=default" in s["start_command"]
|
||||
assert "HERDR_SESSION_NAME=default" in s["attach_command"]
|
||||
assert "HERDR_SESSION_NAME=default" in s["kill_command"]
|
||||
|
||||
|
||||
def test_comp_create_herdr_session_yaml_propagation(mam_sandbox, mock_herdr, mock_agents):
|
||||
"""Verify HERDR_SESSION_NAME propagation into start_command / herdr_session YAML field."""
|
||||
script = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-create" / "scripts" / "create_session.sh"
|
||||
res = subprocess.run([
|
||||
"bash", str(script),
|
||||
"--workspace", str(mam_sandbox),
|
||||
"--agent", "claude",
|
||||
"--role", "Creator",
|
||||
"--session", "custom-proj-creator-claude",
|
||||
"--herdr-session", "isolated-suite-01"
|
||||
], capture_output=True, text=True)
|
||||
assert res.returncode == 0, res.stderr
|
||||
|
||||
yaml_path = mam_sandbox / ".mam" / "agent-sessions.yaml"
|
||||
import yaml
|
||||
with open(yaml_path) as f:
|
||||
data = yaml.safe_load(f)
|
||||
sessions = data.get("herdr_sessions", [])
|
||||
assert len(sessions) == 1
|
||||
s = sessions[0]
|
||||
assert s["name"] == "custom-proj-creator-claude"
|
||||
assert s["herdr_session"] == "isolated-suite-01"
|
||||
assert s["herdr_server"] == "isolated-suite-01"
|
||||
assert "HERDR_SESSION_NAME=isolated-suite-01" in s["start_command"]
|
||||
assert "HERDR_SESSION_NAME=isolated-suite-01" in s["attach_command"]
|
||||
assert "HERDR_SESSION_NAME=isolated-suite-01" in s["kill_command"]
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# FEATURE 2: Resume Session (5 Test Cases)
|
||||
# FEATURE 2: Resume Session (6 Test Cases)
|
||||
# ==============================================================================
|
||||
|
||||
def test_comp_resume_config_restore(mam_sandbox):
|
||||
@@ -293,6 +407,51 @@ d['herdr_sessions'] = [{
|
||||
assert res.stdout.strip() == "scanned-uuid"
|
||||
|
||||
|
||||
def test_comp_resume_herdr_session_propagation(mam_sandbox, mock_herdr, mock_agents):
|
||||
"""Verify that resume_session.sh with --herdr-session updates existing row's herdr_session."""
|
||||
conv_id = "11111111-2222-3333-4444-555555555555"
|
||||
key = str(mam_sandbox).replace('/', '-').replace('_', '-')
|
||||
proj_dir = mam_sandbox / ".claude" / "projects" / key
|
||||
proj_dir.mkdir(parents=True, exist_ok=True)
|
||||
(proj_dir / f"{conv_id}.jsonl").write_text(f'{{"sessionId": "{conv_id}"}}')
|
||||
|
||||
# Seed a stopped session with OLD herdr_session
|
||||
mutation = f"""
|
||||
d['herdr_sessions'] = [{{
|
||||
'name': 'test-proj-creator-claude',
|
||||
'status': 'stopped',
|
||||
'herdr_session': 'OLD-HERDR-SESSION',
|
||||
'herdr_server': 'OLD-HERDR-SESSION',
|
||||
'claude_session_id_own': '{conv_id}',
|
||||
'pane': {{'cwd': '{str(mam_sandbox)}', 'cmd': 'claude'}}
|
||||
}}]
|
||||
"""
|
||||
run_mutation(mam_sandbox, mutation)
|
||||
|
||||
script = mam_sandbox / ".agents" / "skills" / "multi-agent-mux-resume" / "scripts" / "resume_session.sh"
|
||||
res = subprocess.run([
|
||||
"bash", str(script),
|
||||
"--workspace", str(mam_sandbox),
|
||||
"--agent", "claude",
|
||||
"--session", "test-proj-creator-claude",
|
||||
"--herdr-session", "NEW-HERDR-SESSION"
|
||||
], capture_output=True, text=True)
|
||||
assert res.returncode == 0, res.stderr
|
||||
|
||||
yaml_path = mam_sandbox / ".mam" / "agent-sessions.yaml"
|
||||
import yaml
|
||||
with open(yaml_path) as f:
|
||||
data = yaml.safe_load(f)
|
||||
sessions = data.get("herdr_sessions", [])
|
||||
assert len(sessions) == 1
|
||||
s = sessions[0]
|
||||
assert s["status"] == "running"
|
||||
assert s["herdr_session"] == "NEW-HERDR-SESSION"
|
||||
assert s["herdr_server"] == "NEW-HERDR-SESSION"
|
||||
assert "HERDR_SESSION_NAME=NEW-HERDR-SESSION" in s["attach_command"]
|
||||
assert "HERDR_SESSION_NAME=NEW-HERDR-SESSION" in s["kill_command"]
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# FEATURE 3: Stop Session (7 Test Cases)
|
||||
# ==============================================================================
|
||||
@@ -780,7 +939,8 @@ def test_comp_stop_usage_matches_parser(mam_sandbox):
|
||||
for flag, args in (("--reason", ["--reason", "x"]),
|
||||
("--purge-conversation", ["--purge-conversation"]),
|
||||
("--yes", ["--yes"]),
|
||||
("--agent", ["--agent", "hermes"])):
|
||||
("--agent", ["--agent", "hermes"]),
|
||||
("--herdr-session", ["--herdr-session", "isolated-sess"])):
|
||||
r = subprocess.run(["bash", str(script), "--session", VALID] + args,
|
||||
capture_output=True, text=True)
|
||||
assert "unknown arg" not in r.stderr, f"usage() advertises {flag} but parser rejects it: {r.stderr}"
|
||||
|
||||
Reference in New Issue
Block a user