feat(skill): implement multi-agent-mux-orc-onboard skill and orchestrator_uuids exclusion gate (40/40 PASS)
This commit is contained in:
@@ -0,0 +1,606 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import shutil
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).parent.parent
|
||||
LIB_SH = REPO_ROOT / ".agents" / "skills" / "lib.sh"
|
||||
ONBOARD_SH = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-orc-onboard" / "scripts" / "orc_onboard.sh"
|
||||
RECONCILE_SH = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-monitor" / "scripts" / "reconcile.sh"
|
||||
|
||||
SCRUBBED_ENV = dict(os.environ)
|
||||
for k in ["CLAUDE_CODE_SESSION_ID", "ANTIGRAVITY_CONVERSATION_ID", "HERMES_SESSION_ID", "CLINE_SESSION_ID", "MAM_ORCHESTRATOR_UUIDS"]:
|
||||
SCRUBBED_ENV.pop(k, None)
|
||||
SCRUBBED_ENV["PYTHONPATH"] = f"{LIB_SH.parent}:{SCRUBBED_ENV.get('PYTHONPATH', '')}"
|
||||
|
||||
|
||||
def run_onboard(args, env=None, cwd=None):
|
||||
run_env = dict(SCRUBBED_ENV)
|
||||
if cwd:
|
||||
run_env["AGENT_SESSIONS_YAML"] = f"{cwd}/.mam/agent-sessions.yaml"
|
||||
run_env["YAML_PATH"] = f"{cwd}/.mam/agent-sessions.yaml"
|
||||
run_env["HOME_DIR"] = cwd
|
||||
if env:
|
||||
run_env.update(env)
|
||||
cmd = ["bash", str(ONBOARD_SH)] + list(args)
|
||||
return subprocess.run(cmd, capture_output=True, text=True, env=run_env, cwd=cwd)
|
||||
|
||||
|
||||
def run_find_uuid(ws, agent, target="", env=None):
|
||||
run_env = dict(SCRUBBED_ENV)
|
||||
run_env["AGENT_SESSIONS_YAML"] = f"{ws}/.mam/agent-sessions.yaml"
|
||||
run_env["YAML_PATH"] = f"{ws}/.mam/agent-sessions.yaml"
|
||||
run_env["HOME_DIR"] = ws
|
||||
run_env["CLAUDE_PROJECT_DIR"] = f"{ws}/.claude/projects"
|
||||
if env:
|
||||
run_env.update(env)
|
||||
cmd = ["bash", "-c", f"source '{LIB_SH}' && find_workspace_uuid '{ws}' '{agent}' '{target}'"]
|
||||
res = subprocess.run(cmd, capture_output=True, text=True, env=run_env)
|
||||
return res.stdout.strip()
|
||||
|
||||
|
||||
def run_verify_uuid(ws, agent, uuid, row=None, mode="discover", env=None):
|
||||
run_env = dict(SCRUBBED_ENV)
|
||||
run_env["AGENT_SESSIONS_YAML"] = f"{ws}/.mam/agent-sessions.yaml"
|
||||
run_env["YAML_PATH"] = f"{ws}/.mam/agent-sessions.yaml"
|
||||
run_env["HOME_DIR"] = ws
|
||||
run_env["CLAUDE_PROJECT_DIR"] = f"{ws}/.claude/projects"
|
||||
if env:
|
||||
run_env.update(env)
|
||||
row_json = json.dumps(row) if row else "{}"
|
||||
cmd = ["bash", "-c", f"source '{LIB_SH}' && verify_session_uuid '{ws}' '{agent}' '{uuid}' '{row_json}' '{mode}'"]
|
||||
res = subprocess.run(cmd, capture_output=True, text=True, env=run_env)
|
||||
return res.returncode == 0
|
||||
|
||||
|
||||
def dump_state(mam_sandbox, state):
|
||||
yaml_path = str(mam_sandbox / ".mam" / "agent-sessions.yaml")
|
||||
env = {"AGENT_SESSIONS_YAML": yaml_path, "YAML_PATH": yaml_path, "HOME_DIR": str(mam_sandbox)}
|
||||
state_str = json.dumps(state)
|
||||
cmd = f"source '{LIB_SH}' && atomic_dump_yaml '{yaml_path}' <<'PYEOF'\nd.update({state_str})\nPYEOF"
|
||||
subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, **env}, check=True)
|
||||
return env
|
||||
|
||||
|
||||
def make_claude_transcript(mam_sandbox, uuid, target_dir=None):
|
||||
key = str(mam_sandbox.resolve()).replace("/", "-").replace("_", "-")
|
||||
if target_dir:
|
||||
c_dir = target_dir / "projects" / key
|
||||
else:
|
||||
c_dir = mam_sandbox / ".claude" / "projects" / key
|
||||
c_dir.mkdir(parents=True, exist_ok=True)
|
||||
t_file = c_dir / f"{uuid}.jsonl"
|
||||
t_file.write_text(f'{{"sessionId": "{uuid}", "cwd": "{mam_sandbox}"}}\n')
|
||||
return t_file
|
||||
|
||||
|
||||
# O-1: find_workspace_uuid excludes orchestrator UUID in discover mode
|
||||
def test_o1_find_workspace_uuid_excludes_orchestrator(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid)
|
||||
|
||||
state = {
|
||||
"orchestrator_uuids": [orc_uuid],
|
||||
"herdr_sessions": []
|
||||
}
|
||||
env = dump_state(mam_sandbox, state)
|
||||
found = run_find_uuid(str(mam_sandbox), "claude", env=env)
|
||||
assert found != orc_uuid
|
||||
|
||||
|
||||
# O-2: find_workspace_uuid preserves target row own ID even if in orchestrator_uuids
|
||||
def test_o2_find_workspace_uuid_preserves_own_id(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid)
|
||||
|
||||
state = {
|
||||
"orchestrator_uuids": [orc_uuid],
|
||||
"herdr_sessions": [{
|
||||
"name": "my-orc-session",
|
||||
"status": "running",
|
||||
"claude_session_id_own": orc_uuid,
|
||||
"pane": {"cwd": str(mam_sandbox)}
|
||||
}]
|
||||
}
|
||||
env = dump_state(mam_sandbox, state)
|
||||
found = run_find_uuid(str(mam_sandbox), "claude", target="my-orc-session", env=env)
|
||||
assert found == orc_uuid
|
||||
|
||||
|
||||
# O-3: Baseline non-orchestrator transcript is found
|
||||
def test_o3_find_workspace_uuid_finds_normal_subagent(mam_sandbox):
|
||||
sub_uuid = "02222222-2222-2222-2222-222222222222"
|
||||
make_claude_transcript(mam_sandbox, sub_uuid)
|
||||
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
found = run_find_uuid(str(mam_sandbox), "claude", env=env)
|
||||
assert found == sub_uuid
|
||||
|
||||
|
||||
# O-4: find_workspace_uuid without target excludes orchestrator_uuids
|
||||
def test_o4_find_workspace_uuid_without_target_excludes_orc(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid)
|
||||
|
||||
state = {"orchestrator_uuids": [orc_uuid], "herdr_sessions": []}
|
||||
env = dump_state(mam_sandbox, state)
|
||||
found = run_find_uuid(str(mam_sandbox), "claude", target="", env=env)
|
||||
assert found == ""
|
||||
|
||||
|
||||
# O-5: MAM_ORCHESTRATOR_UUIDS env var override
|
||||
def test_o5_env_override_orchestrator_uuids(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid)
|
||||
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"MAM_ORCHESTRATOR_UUIDS": orc_uuid
|
||||
}
|
||||
found = run_find_uuid(str(mam_sandbox), "claude", env=env)
|
||||
assert found == ""
|
||||
|
||||
|
||||
# O-6: Empty MAM_ORCHESTRATOR_UUIDS disables gate
|
||||
def test_o6_empty_env_override_disables_gate(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid)
|
||||
|
||||
state = {"orchestrator_uuids": [orc_uuid], "herdr_sessions": []}
|
||||
env = dump_state(mam_sandbox, state)
|
||||
env["MAM_ORCHESTRATOR_UUIDS"] = ""
|
||||
|
||||
found = run_find_uuid(str(mam_sandbox), "claude", env=env)
|
||||
assert found == orc_uuid
|
||||
|
||||
|
||||
# O-7: JSON array format in MAM_ORCHESTRATOR_UUIDS
|
||||
def test_o7_json_array_env_override(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid)
|
||||
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"MAM_ORCHESTRATOR_UUIDS": f'["{orc_uuid}"]'
|
||||
}
|
||||
found = run_find_uuid(str(mam_sandbox), "claude", env=env)
|
||||
assert found == ""
|
||||
|
||||
|
||||
# O-8: verify_session_uuid in revalidate mode permits orchestrator_uuids for own row
|
||||
def test_o8_revalidate_mode_permits_orchestrator_uuid(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid)
|
||||
|
||||
row = {
|
||||
"name": "my-orc",
|
||||
"claude_session_id_own": orc_uuid,
|
||||
"pane": {"cwd": str(mam_sandbox)}
|
||||
}
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"MAM_ORCHESTRATOR_UUIDS": orc_uuid
|
||||
}
|
||||
assert run_verify_uuid(str(mam_sandbox), "claude", orc_uuid, row=row, mode="revalidate", env=env)
|
||||
|
||||
|
||||
# O-9: verify_session_uuid in discover mode blocks orchestrator_uuids for unowned row
|
||||
def test_o9_discover_mode_blocks_orchestrator_uuid(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid)
|
||||
|
||||
row = {
|
||||
"name": "subagent-session",
|
||||
"claude_session_id_own": None,
|
||||
"pane": {"cwd": str(mam_sandbox)}
|
||||
}
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"MAM_ORCHESTRATOR_UUIDS": orc_uuid
|
||||
}
|
||||
assert not run_verify_uuid(str(mam_sandbox), "claude", orc_uuid, row=row, mode="discover", env=env)
|
||||
|
||||
|
||||
# O-10: Baseline revalidate mode passes for non-orchestrator
|
||||
def test_o10_revalidate_normal_subagent(mam_sandbox):
|
||||
sub_uuid = "02222222-2222-2222-2222-222222222222"
|
||||
make_claude_transcript(mam_sandbox, sub_uuid)
|
||||
|
||||
row = {"name": "subagent", "claude_session_id_own": sub_uuid, "pane": {"cwd": str(mam_sandbox)}}
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
assert run_verify_uuid(str(mam_sandbox), "claude", sub_uuid, row=row, mode="revalidate", env=env)
|
||||
|
||||
|
||||
# O-11: verify_session_uuid respects isolation root
|
||||
def test_o11_isolation_root_respected(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
iso_root = mam_sandbox / "iso"
|
||||
make_claude_transcript(mam_sandbox, orc_uuid, target_dir=iso_root)
|
||||
|
||||
row = {
|
||||
"name": "my-orc",
|
||||
"claude_session_id_own": orc_uuid,
|
||||
"isolation": {"root": str(iso_root), "uuid": "iso-1"},
|
||||
"pane": {"cwd": str(mam_sandbox)}
|
||||
}
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
assert run_verify_uuid(str(mam_sandbox), "claude", orc_uuid, row=row, mode="revalidate", env=env)
|
||||
|
||||
|
||||
# O-12: orc_onboard.sh --uuid explicitly adds UUID
|
||||
def test_o12_onboard_add_uuid(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
res = run_onboard(["--uuid", orc_uuid], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
assert "Successfully added" in res.stdout or "Successfully" in res.stdout
|
||||
|
||||
# Verify list
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert orc_uuid in res_list.stdout
|
||||
|
||||
|
||||
# O-13: orc_onboard.sh --remove removes UUID
|
||||
def test_o13_onboard_remove_uuid(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
run_onboard(["--uuid", orc_uuid], env=env, cwd=str(mam_sandbox))
|
||||
|
||||
res = run_onboard(["--remove", orc_uuid], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert orc_uuid not in res_list.stdout
|
||||
|
||||
|
||||
# O-14: orc_onboard.sh --dry-run does not mutate state
|
||||
def test_o14_onboard_dry_run(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
res = run_onboard(["--uuid", orc_uuid, "--dry-run"], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
assert "[dry-run]" in res.stdout
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert orc_uuid not in res_list.stdout
|
||||
|
||||
|
||||
# O-15: orc_onboard.sh duplicate add is idempotent
|
||||
def test_o15_onboard_duplicate_add(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
run_onboard(["--uuid", orc_uuid], env=env, cwd=str(mam_sandbox))
|
||||
res = run_onboard(["--uuid", orc_uuid], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert res_list.stdout.count(orc_uuid) == 1
|
||||
|
||||
|
||||
# O-16: orc_onboard.sh rejects invalid UUID format (exit 2)
|
||||
def test_o16_onboard_invalid_uuid_rejected(mam_sandbox):
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
res = run_onboard(["--uuid", "not-a-valid-uuid"], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 2
|
||||
|
||||
|
||||
# O-17: orc_onboard.sh rejects adding ID owned by a running session (exit 1)
|
||||
def test_o17_onboard_conflict_running_session(mam_sandbox):
|
||||
sub_uuid = "02222222-2222-2222-2222-222222222222"
|
||||
state = {
|
||||
"orchestrator_uuids": [],
|
||||
"herdr_sessions": [{
|
||||
"name": "active-subagent",
|
||||
"status": "running",
|
||||
"claude_session_id_own": sub_uuid,
|
||||
"pane": {"cwd": str(mam_sandbox)}
|
||||
}]
|
||||
}
|
||||
env = dump_state(mam_sandbox, state)
|
||||
res = run_onboard(["--uuid", sub_uuid], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 1
|
||||
|
||||
|
||||
# O-18: orc_onboard.sh autodetects via environment variable (CLAUDE_CODE_SESSION_ID)
|
||||
def test_o18_autodetect_via_env(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"CLAUDE_CODE_SESSION_ID": orc_uuid
|
||||
}
|
||||
# Run inside subshell where command line matches claude
|
||||
cmd = f"export CLAUDE_CODE_SESSION_ID='{orc_uuid}' && exec -a claude bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert orc_uuid in res_list.stdout
|
||||
|
||||
|
||||
# O-19: orc_onboard.sh exit 3 when autodetect fails
|
||||
def test_o19_autodetect_failure_exits_3(mam_sandbox):
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox), "MAM_AUTODETECT_FORCE_FAIL": "1"}
|
||||
res = run_onboard([], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 3
|
||||
|
||||
|
||||
# O-20: _validate fails on malformed orchestrator_uuids (not a list)
|
||||
def test_o20_validate_fails_on_non_list_orchestrator_uuids(mam_sandbox):
|
||||
state = {"orchestrator_uuids": "not-a-list", "herdr_sessions": []}
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "YAML_PATH": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
state_str = json.dumps(state)
|
||||
cmd = f"source '{LIB_SH}' && atomic_dump_yaml '{env['YAML_PATH']}' <<'PYEOF'\nd.update({state_str})\nPYEOF"
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, **env}, capture_output=True, text=True)
|
||||
assert res.returncode != 0
|
||||
assert "VALIDATE" in res.stderr or "VALIDATE" in res.stdout
|
||||
|
||||
|
||||
# O-21: _validate fails on duplicate orchestrator_uuids
|
||||
def test_o21_validate_fails_on_duplicate_orchestrator_uuids(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
state = {"orchestrator_uuids": [orc_uuid, orc_uuid], "herdr_sessions": []}
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "YAML_PATH": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
state_str = json.dumps(state)
|
||||
cmd = f"source '{LIB_SH}' && atomic_dump_yaml '{env['YAML_PATH']}' <<'PYEOF'\nd.update({state_str})\nPYEOF"
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, **env}, capture_output=True, text=True)
|
||||
assert res.returncode != 0
|
||||
|
||||
|
||||
# O-22: _validate fails on empty string in orchestrator_uuids
|
||||
def test_o22_validate_fails_on_empty_string_orchestrator_uuid(mam_sandbox):
|
||||
state = {"orchestrator_uuids": [""], "herdr_sessions": []}
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "YAML_PATH": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
state_str = json.dumps(state)
|
||||
cmd = f"source '{LIB_SH}' && atomic_dump_yaml '{env['YAML_PATH']}' <<'PYEOF'\nd.update({state_str})\nPYEOF"
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, **env}, capture_output=True, text=True)
|
||||
assert res.returncode != 0
|
||||
|
||||
|
||||
# O-23: get_all_sessions_status folds orchestrator_uuids into hash
|
||||
def test_o23_status_hash_includes_orchestrator_uuids(mam_sandbox):
|
||||
state1 = {"orchestrator_uuids": ["01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"], "herdr_sessions": []}
|
||||
state2 = {"orchestrator_uuids": ["02222222-2222-2222-2222-222222222222"], "herdr_sessions": []}
|
||||
yaml_path = str(mam_sandbox / ".mam" / "agent-sessions.yaml")
|
||||
|
||||
cmd = f"""source '{LIB_SH}' && atomic_dump_yaml '{yaml_path}' <<'PYEOF'
|
||||
import json, sys
|
||||
h1 = get_all_sessions_status({json.dumps(state1)})
|
||||
h2 = get_all_sessions_status({json.dumps(state2)})
|
||||
if h1 == h2:
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
"""
|
||||
res = subprocess.run(["bash", "-c", cmd], env={**SCRUBBED_ENV, "AGENT_SESSIONS_YAML": yaml_path, "YAML_PATH": yaml_path}, capture_output=True, text=True)
|
||||
assert res.returncode == 0
|
||||
|
||||
|
||||
# O-24: Substring matching prevented in verify_session_uuid
|
||||
def test_o24_substring_matching_prevented(mam_sandbox):
|
||||
full_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
sub_str = "01eae7cf"
|
||||
make_claude_transcript(mam_sandbox, sub_str)
|
||||
|
||||
row = {"name": "subagent", "claude_session_id_own": None, "pane": {"cwd": str(mam_sandbox)}}
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"MAM_ORCHESTRATOR_UUIDS": full_uuid
|
||||
}
|
||||
# sub_str is not full_uuid -> discover mode should NOT block sub_str
|
||||
assert run_verify_uuid(str(mam_sandbox), "claude", sub_str, row=row, mode="discover", env=env)
|
||||
|
||||
|
||||
# O-25: agy agent family orchestrator exclusion
|
||||
def test_o25_agy_orchestrator_exclusion(mam_sandbox):
|
||||
orc_uuid = "72d2d251-5a06-486b-92e5-7e46a7a80d2e"
|
||||
db_dir = mam_sandbox / ".gemini" / "antigravity-cli" / "conversations"
|
||||
db_dir.mkdir(parents=True, exist_ok=True)
|
||||
db_path = db_dir / f"{orc_uuid}.db"
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute("CREATE TABLE steps (id INT)")
|
||||
conn.execute("INSERT INTO steps VALUES (1)")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
lc_path = mam_sandbox / ".gemini" / "antigravity-cli" / "cache" / "last_conversations.json"
|
||||
lc_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
lc_path.write_text(json.dumps({str(mam_sandbox): orc_uuid}))
|
||||
|
||||
state = {"orchestrator_uuids": [orc_uuid], "herdr_sessions": []}
|
||||
env = dump_state(mam_sandbox, state)
|
||||
found = run_find_uuid(str(mam_sandbox), "agy", env=env)
|
||||
assert found != orc_uuid
|
||||
|
||||
|
||||
# O-26: hermes agent family orchestrator exclusion
|
||||
def test_o26_hermes_orchestrator_exclusion(mam_sandbox):
|
||||
orc_uuid = "03333333-3333-3333-3333-333333333333"
|
||||
hdb_dir = mam_sandbox / ".hermes"
|
||||
hdb_dir.mkdir(parents=True, exist_ok=True)
|
||||
hdb_path = hdb_dir / "state.db"
|
||||
|
||||
conn = sqlite3.connect(hdb_path)
|
||||
conn.execute("CREATE TABLE sessions (id TEXT, cwd TEXT, started_at TEXT)")
|
||||
conn.execute("INSERT INTO sessions VALUES (?, ?, ?)", (orc_uuid, str(mam_sandbox), "2026-08-12T00:00:00Z"))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
state = {"orchestrator_uuids": [orc_uuid], "herdr_sessions": []}
|
||||
env = dump_state(mam_sandbox, state)
|
||||
row = {"name": "subagent", "hermes_conversation_id_own": None, "pane": {"cwd": str(mam_sandbox)}}
|
||||
assert not run_verify_uuid(str(mam_sandbox), "hermes", orc_uuid, row=row, mode="discover", env=env)
|
||||
|
||||
|
||||
# O-27: Ignore env variable belonging to a different agent family (Challenge fix)
|
||||
def test_o27_ignore_different_family_env_variable(mam_sandbox):
|
||||
# Under claude process ancestor, ANTIGRAVITY_CONVERSATION_ID should be ignored
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"ANTIGRAVITY_CONVERSATION_ID": "72d2d251-5a06-486b-92e5-7e46a7a80d2e"
|
||||
}
|
||||
cmd = f"export ANTIGRAVITY_CONVERSATION_ID='72d2d251-5a06-486b-92e5-7e46a7a80d2e' && exec -a claude bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 3
|
||||
|
||||
|
||||
# O-28: Use env variable when agent family matches
|
||||
def test_o28_use_family_matched_env_variable(mam_sandbox):
|
||||
orc_uuid = "72d2d251-5a06-486b-92e5-7e46a7a80d2e"
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"ANTIGRAVITY_CONVERSATION_ID": orc_uuid
|
||||
}
|
||||
cmd = f"export ANTIGRAVITY_CONVERSATION_ID='{orc_uuid}' && exec -a agy bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert orc_uuid in res_list.stdout
|
||||
|
||||
|
||||
# O-29: Fresh claude session detected via CLAUDE_CODE_SESSION_ID
|
||||
def test_o29_fresh_claude_session_detection(mam_sandbox):
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"CLAUDE_CODE_SESSION_ID": orc_uuid
|
||||
}
|
||||
cmd = f"export CLAUDE_CODE_SESSION_ID='{orc_uuid}' && exec -a claude bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert orc_uuid in res_list.stdout
|
||||
|
||||
|
||||
# O-30: argv takes precedence over env variable
|
||||
def test_o30_argv_precedes_env(mam_sandbox):
|
||||
argv_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
env_uuid = "02222222-2222-2222-2222-222222222222"
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox),
|
||||
"CLAUDE_CODE_SESSION_ID": env_uuid
|
||||
}
|
||||
cmd = f"export CLAUDE_CODE_SESSION_ID='{env_uuid}' && exec -a claude bash '{ONBOARD_SH}' --uuid {argv_uuid}"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert argv_uuid in res_list.stdout
|
||||
assert env_uuid not in res_list.stdout
|
||||
|
||||
|
||||
# O-31: cline node launcher & non-UUID ID format (1785635248957_fajon)
|
||||
def test_o31_cline_node_launcher_id_format(mam_sandbox):
|
||||
cline_id = "1785635248957_fajon"
|
||||
env = {
|
||||
"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"),
|
||||
"HOME_DIR": str(mam_sandbox)
|
||||
}
|
||||
cmd = f"exec -a 'cline --id {cline_id}' bash '{ONBOARD_SH}'"
|
||||
res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env={**SCRUBBED_ENV, **env}, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
res_list = run_onboard(["--list"], env=env, cwd=str(mam_sandbox))
|
||||
assert cline_id in res_list.stdout
|
||||
|
||||
|
||||
# O-32: ID format strictness rejects invalid strings
|
||||
def test_o32_id_format_strictness(mam_sandbox):
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
for bad in ["../../etc/passwd", "a b", "fajon", "1785635248957"]:
|
||||
res = run_onboard(["--uuid", bad], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 2
|
||||
|
||||
|
||||
# O-33: No workspace cache fallback (last_conversations.json excluded)
|
||||
def test_o33_no_workspace_cache_fallback(mam_sandbox):
|
||||
lc_path = mam_sandbox / ".gemini" / "antigravity-cli" / "cache" / "last_conversations.json"
|
||||
lc_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
cached_uuid = "0f84dbf7-5ddf-4619-a813-7e1ae35be009"
|
||||
lc_path.write_text(json.dumps({str(mam_sandbox): cached_uuid}))
|
||||
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox), "MAM_AUTODETECT_FORCE_FAIL": "1"}
|
||||
res = run_onboard([], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 3
|
||||
|
||||
|
||||
# O-34: deploy/remove.sh fallback_assets contains multi-agent-mux-orc-onboard
|
||||
def test_o34_remove_sh_fallback_assets():
|
||||
remove_sh = REPO_ROOT / "deploy" / "remove.sh"
|
||||
content = remove_sh.read_text()
|
||||
assert ".agents/skills/multi-agent-mux-orc-onboard" in content
|
||||
|
||||
|
||||
# O-35: deploy/gitea-ci.yml contains shellcheck line for orc_onboard.sh
|
||||
def test_o35_gitea_ci_shellcheck():
|
||||
ci_file = REPO_ROOT / "deploy" / "gitea-ci.yml"
|
||||
content = ci_file.read_text()
|
||||
assert "shellcheck .agents/skills/multi-agent-mux-orc-onboard/scripts/orc_onboard.sh" in content
|
||||
|
||||
|
||||
# O-36: bash syntax check clean on orc_onboard.sh
|
||||
def test_o36_bash_syntax_clean():
|
||||
res = subprocess.run(["bash", "-n", str(ONBOARD_SH)], capture_output=True)
|
||||
assert res.returncode == 0, res.stderr
|
||||
|
||||
|
||||
# O-37: SKILL.md file exists and has valid frontmatter
|
||||
def test_o37_skill_md_valid():
|
||||
skill_md = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-orc-onboard" / "SKILL.md"
|
||||
assert skill_md.is_file()
|
||||
content = skill_md.read_text()
|
||||
assert "name: multi-agent-mux-orc-onboard" in content
|
||||
|
||||
|
||||
# O-38: atomic_dump_yaml handles empty orchestrator_uuids initialization
|
||||
def test_o38_atomic_dump_yaml_initialization(mam_sandbox):
|
||||
env = {"AGENT_SESSIONS_YAML": str(mam_sandbox / ".mam" / "agent-sessions.yaml"), "HOME_DIR": str(mam_sandbox)}
|
||||
orc_uuid = "01eae7cf-1db6-4395-ba48-5fb02f4b6b1f"
|
||||
res = run_onboard(["--uuid", orc_uuid], env=env, cwd=str(mam_sandbox))
|
||||
assert res.returncode == 0
|
||||
|
||||
yaml_path = mam_sandbox / ".mam" / "agent-sessions.yaml"
|
||||
assert yaml_path.is_file()
|
||||
assert orc_uuid in yaml_path.read_text()
|
||||
|
||||
|
||||
# O-39: cline agent family discovery exclusion in verify_session_uuid
|
||||
def test_o39_cline_orchestrator_exclusion(mam_sandbox):
|
||||
cline_id = "1785635248957_fajon"
|
||||
c_dir = mam_sandbox / ".cline" / "data" / "sessions" / cline_id
|
||||
c_dir.mkdir(parents=True, exist_ok=True)
|
||||
(c_dir / f"{cline_id}.json").write_text(json.dumps({"session_id": cline_id}))
|
||||
|
||||
state = {"orchestrator_uuids": [cline_id], "herdr_sessions": []}
|
||||
env = dump_state(mam_sandbox, state)
|
||||
row = {"name": "subagent", "cline_conversation_id_own": None, "pane": {"cwd": str(mam_sandbox)}}
|
||||
assert not run_verify_uuid(str(mam_sandbox), "cline", cline_id, row=row, mode="discover", env=env)
|
||||
|
||||
|
||||
# O-40: Malformed orchestrator_uuids logs warning to stderr and degrades gracefully
|
||||
def test_o40_malformed_orchestrator_uuids_degrades_open(mam_sandbox):
|
||||
sub_uuid = "02222222-2222-2222-2222-222222222222"
|
||||
make_claude_transcript(mam_sandbox, sub_uuid)
|
||||
|
||||
yaml_path = mam_sandbox / ".mam" / "agent-sessions.yaml"
|
||||
yaml_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
yaml_path.write_text("orchestrator_uuids: 12345\nherdr_sessions: []\n")
|
||||
|
||||
env = {"AGENT_SESSIONS_YAML": str(yaml_path), "HOME_DIR": str(mam_sandbox)}
|
||||
found = run_find_uuid(str(mam_sandbox), "claude", env=env)
|
||||
assert found == sub_uuid
|
||||
Reference in New Issue
Block a user