import os import sys import subprocess import time import pytest import shutil from pathlib import Path REPO_ROOT = Path(__file__).parent.parent LOCK_SCRIPT = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "loop_lock.sh" RUN_LOOP = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "run_loop.sh" def run_lock_cmd(action, marker_path, env=None): cmd = ["bash", str(LOCK_SCRIPT), action, str(marker_path)] run_env = dict(os.environ) if env: run_env.update(env) res = subprocess.run(cmd, capture_output=True, text=True, env=run_env) return res def acquire_bg(marker_path, sleep_secs=10): cmd = f"source '{LOCK_SCRIPT}' && mam_acquire_loop_lock '{marker_path}' && sleep {sleep_secs}" proc = subprocess.Popen(["bash", "-c", cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) time.sleep(0.3) return proc def get_lstart(pid=None): if pid is None: pid = os.getpid() res = subprocess.run(["ps", "-p", str(pid), "-o", "lstart="], capture_output=True, text=True) return res.stdout.strip() def test_o2_1_acquire_empty_marker(tmp_path): marker = tmp_path / "loop-guard-active" proc = acquire_bg(marker) try: assert marker.is_file() content = marker.read_text() assert f"pid={proc.pid}" in content assert "lstart=" in content assert "started=" in content finally: proc.kill() proc.wait() def test_o2_2_live_holder_rejected(tmp_path): marker = tmp_path / "loop-guard-active" proc = acquire_bg(marker) try: res = run_lock_cmd("acquire", marker) assert res.returncode == 3 assert f"pid={proc.pid}" in marker.read_text() finally: proc.kill() proc.wait() def test_o2_3_dead_holder_reclaimed(tmp_path): marker = tmp_path / "loop-guard-active" marker.write_text("pid=999999\nlstart=Sat Jan 1 00:00:00 2000\nstarted=2000-01-01T00:00:00Z\n") proc = acquire_bg(marker) try: assert marker.is_file() assert f"pid={proc.pid}" in marker.read_text() finally: proc.kill() proc.wait() def test_o2_4_pid_rollover_reclaimed(tmp_path): marker = tmp_path / "loop-guard-active" proc_dummy = subprocess.Popen(["sleep", "10"]) try: # Same PID as proc_dummy, but lstart differs -> stale! marker.write_text(f"pid={proc_dummy.pid}\nlstart=Sat Jan 1 00:00:00 2000\nstarted=2000-01-01T00:00:00Z\n") proc = acquire_bg(marker) try: assert marker.is_file() assert f"pid={proc.pid}" in marker.read_text() finally: proc.kill() proc.wait() finally: proc_dummy.kill() proc_dummy.wait() def test_o2_5_release_other_marker_fails(tmp_path): marker = tmp_path / "loop-guard-active" proc = acquire_bg(marker) try: res = run_lock_cmd("release", marker) assert res.returncode == 1 assert marker.is_file() finally: proc.kill() proc.wait() def test_o2_6_release_own_marker_succeeds(tmp_path): marker = tmp_path / "loop-guard-active" cmd = f"source '{LOCK_SCRIPT}' && mam_acquire_loop_lock '{marker}' && mam_release_loop_lock '{marker}'" res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True) assert res.returncode == 0 assert not marker.exists() def test_o2_7_no_leftover_temp_files(tmp_path): marker = tmp_path / "loop-guard-active" cmd = f"source '{LOCK_SCRIPT}' && mam_acquire_loop_lock '{marker}' && mam_release_loop_lock '{marker}'" subprocess.run(["bash", "-c", cmd], capture_output=True, text=True) files = list(tmp_path.glob("*")) assert files == [] def test_o2_8_concurrent_acquisition_single_winner(tmp_path): marker = tmp_path / "loop-guard-active" script = f""" source '{LOCK_SCRIPT}' if mam_acquire_loop_lock '{marker}'; then sleep 2 exit 0 else exit 3 fi """ procs = [] for _ in range(8): p = subprocess.Popen(["bash", "-c", script], stdout=subprocess.PIPE, stderr=subprocess.PIPE) procs.append(p) rcs = [p.wait() for p in procs] winners = [rc for rc in rcs if rc == 0] losers = [rc for rc in rcs if rc == 3] assert len(winners) == 1 assert len(losers) == 7 def test_o2_9_marker_never_observed_partial(tmp_path): marker = tmp_path / "loop-guard-active" proc = acquire_bg(marker) try: content = marker.read_text() assert content.startswith("pid=") assert "lstart=" in content finally: proc.kill() proc.wait() def test_o2_10_ps_unavailable_fails_safe(tmp_path): marker = tmp_path / "loop-guard-active" proc = acquire_bg(marker) try: cmd = f"export PATH=/bin/nonexistent:$PATH && source '{LOCK_SCRIPT}' && mam_acquire_loop_lock '{marker}'" res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True) assert res.returncode in (3, 4) finally: proc.kill() proc.wait() def test_o2_11_legacy_marker_respected(tmp_path): marker = tmp_path / "loop-guard-active" proc_dummy = subprocess.Popen(["sleep", "10"]) try: marker.write_text(f"pid={proc_dummy.pid}\n") cmd = f"source '{LOCK_SCRIPT}' && mam_acquire_loop_lock '{marker}'" res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True) assert res.returncode == 3 finally: proc_dummy.kill() proc_dummy.wait() def test_o2_12_run_loop_exits_on_lock_failure(tmp_path): marker = tmp_path / "loop-guard-active" proc = acquire_bg(marker) try: cmd = ["bash", str(RUN_LOOP), "--creator", "dummy-agent", "--task", "test"] run_env = dict(os.environ) run_env["MAM_LOOP_MARKER"] = str(marker) res = subprocess.run(cmd, capture_output=True, text=True, cwd=str(tmp_path), env=run_env) assert res.returncode != 0 assert "Another multi-agent-mux-loop is already running" in res.stdout or "Another multi-agent-mux-loop is already running" in res.stderr finally: proc.kill() proc.wait() def test_o2_13_o3_guard_compatibility(mam_sandbox): marker = mam_sandbox / ".mam" / "loop-guard-active" marker.parent.mkdir(parents=True, exist_ok=True) proc = acquire_bg(marker) try: guard_script = REPO_ROOT / ".agents" / "hooks" / "loop_delegation_guard.sh" payload = f'{{"toolCall":{{"name":"file_change"}},"workspacePaths":["{mam_sandbox}"]}}' res = subprocess.run(["bash", str(guard_script)], input=payload, capture_output=True, text=True, cwd=str(mam_sandbox)) assert res.returncode == 0 assert '"decision":"deny"' in res.stdout.replace(" ", "") finally: proc.kill() proc.wait() def test_o2_14_bash_syntax_clean(): res1 = subprocess.run(["bash", "-n", str(LOCK_SCRIPT)], capture_output=True) res2 = subprocess.run(["bash", "-n", str(RUN_LOOP)], capture_output=True) assert res1.returncode == 0, res1.stderr assert res2.returncode == 0, res2.stderr def test_o2_15_rejected_loop_preserves_holder_marker(tmp_path): marker = tmp_path / "loop-guard-active" proc = acquire_bg(marker) try: holder_content = marker.read_text() # Subshell attempt that gets rejected cmd = f"source '{LOCK_SCRIPT}' && (mam_acquire_loop_lock '{marker}' || true) && mam_release_loop_lock '{marker}'" subprocess.run(["bash", "-c", cmd], capture_output=True, text=True) assert marker.is_file() assert marker.read_text() == holder_content finally: proc.kill() proc.wait() def test_o2_16_concurrent_stale_recovery(tmp_path): marker = tmp_path / "loop-guard-active" marker.write_text("pid=999999\nlstart=Sat Jan 1 00:00:00 2000\n") script = f""" source '{LOCK_SCRIPT}' if mam_acquire_loop_lock '{marker}'; then sleep 2 exit 0 else exit 3 fi """ procs = [] for _ in range(6): p = subprocess.Popen(["bash", "-c", script], stdout=subprocess.PIPE, stderr=subprocess.PIPE) procs.append(p) rcs = [p.wait() for p in procs] winners = [rc for rc in rcs if rc == 0] assert len(winners) == 1 def test_o2_17_staggered_stale_recovery(tmp_path): marker = tmp_path / "loop-guard-active" marker.write_text("pid=999999\nlstart=Sat Jan 1 00:00:00 2000\n") procs = [] for i in range(5): script = f""" sleep 0.05 source '{LOCK_SCRIPT}' if mam_acquire_loop_lock '{marker}'; then sleep 2 exit 0 else exit 3 fi """ p = subprocess.Popen(["bash", "-c", script], stdout=subprocess.PIPE, stderr=subprocess.PIPE) procs.append(p) rcs = [p.wait() for p in procs] winners = [rc for rc in rcs if rc == 0] assert len(winners) == 1 def test_o2_18_orphan_steal_lock_recovered(tmp_path): marker = tmp_path / "loop-guard-active" marker.write_text("pid=999999\nlstart=Sat Jan 1 00:00:00 2000\n") steal_lock = tmp_path / "loop-guard-active.steal" steal_lock.write_text("pid=999998\nlstart=Sat Jan 1 00:00:00 2000\n") proc = acquire_bg(marker) try: assert marker.is_file() assert f"pid={proc.pid}" in marker.read_text() finally: proc.kill() proc.wait() def test_o2_19_active_steal_in_progress_not_displaced(tmp_path): marker = tmp_path / "loop-guard-active" marker.write_text("pid=999999\nlstart=Sat Jan 1 00:00:00 2000\n") steal_lock = tmp_path / "loop-guard-active.steal" proc_steal = acquire_bg(steal_lock) try: cmd = f"source '{LOCK_SCRIPT}' && mam_acquire_loop_lock '{marker}'" res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True) assert res.returncode == 3 finally: proc_steal.kill() proc_steal.wait() def test_o2_20_contentless_steal_lock_reclaimed(tmp_path): marker = tmp_path / "loop-guard-active" marker.write_text("pid=999999\nlstart=Sat Jan 1 00:00:00 2000\n") steal_lock = tmp_path / "loop-guard-active.steal" steal_lock.write_text("") proc = acquire_bg(marker) try: assert marker.is_file() assert f"pid={proc.pid}" in marker.read_text() finally: proc.kill() proc.wait() def test_o2_21_legacy_steal_directory_cleaned(tmp_path): marker = tmp_path / "loop-guard-active" steal_dir = tmp_path / "loop-guard-active.steal" steal_dir.mkdir() proc = acquire_bg(marker) try: assert marker.is_file() assert not steal_dir.exists() finally: proc.kill() proc.wait() def test_o2_22_repeated_invocations_converge(tmp_path): marker = tmp_path / "loop-guard-active" for i in range(5): cmd = f"source '{LOCK_SCRIPT}' && mam_acquire_loop_lock '{marker}' && mam_release_loop_lock '{marker}'" res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True) assert res.returncode == 0 assert not marker.exists()