Files
multi-agent-mux/tests/test_orc_onboard.py
T

607 lines
26 KiB
Python

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