fix(loop): eliminate tmp script copy and trap leak in delegate_job_safe (P2-1/B-6)
This commit is contained in:
@@ -147,16 +147,171 @@ def test_z8_dead_pid_marker(mam_sandbox):
|
||||
assert res["decision"] == "allow"
|
||||
|
||||
|
||||
# Z-9: delegate_job_safe restores _mam_release_guard trap
|
||||
def test_z9_delegate_job_safe_restores_trap():
|
||||
def _extract_delegate_job_safe():
|
||||
run_loop = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "run_loop.sh"
|
||||
content = run_loop.read_text()
|
||||
# Check inside delegate_job_safe definition that trap - is replaced with trap _mam_release_guard
|
||||
func_start = content.find("delegate_job_safe() {")
|
||||
assert func_start != -1
|
||||
func_body = content[func_start:func_start+400]
|
||||
assert "trap _mam_release_guard EXIT INT TERM HUP" in func_body
|
||||
assert "trap - EXIT INT TERM HUP" not in func_body
|
||||
func_end = content.find("\n}\n", func_start)
|
||||
assert func_end != -1
|
||||
return content[func_start:func_end + 3]
|
||||
|
||||
|
||||
# Z-9: delegate_job_safe preserves loop lock across delegations without tmp copies/traps (B-6, D1)
|
||||
def test_z9_loop_lock_survives_delegation(tmp_path):
|
||||
loop_lock_sh = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "loop_lock.sh"
|
||||
skill_dir = tmp_path / ".agents" / "skills" / "multi-agent-mux-delegate-job"
|
||||
skill_dir.mkdir(parents=True)
|
||||
stub_wrapper = skill_dir / "multi-agent-mux-delegate-job"
|
||||
stub_wrapper.write_text("#!/usr/bin/env bash\necho 'JOB_ID: 12345678'\n")
|
||||
stub_wrapper.chmod(0o755)
|
||||
|
||||
extracted_func = _extract_delegate_job_safe()
|
||||
script = f"""#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
REPO_ROOT="{tmp_path}"
|
||||
MAM_LOOP_MARKER="$REPO_ROOT/.mam/loop-guard-active"
|
||||
source "{loop_lock_sh}"
|
||||
|
||||
log_error() {{ echo "ERROR: $@" >&2; }}
|
||||
log_info() {{ echo "INFO: $@" >&2; }}
|
||||
|
||||
_mam_release_guard() {{ mam_release_loop_lock "$MAM_LOOP_MARKER" || true; }}
|
||||
|
||||
mam_acquire_loop_lock "$MAM_LOOP_MARKER"
|
||||
trap _mam_release_guard EXIT INT TERM HUP
|
||||
|
||||
{extracted_func}
|
||||
|
||||
PLAN_JOB_OUTPUT=$(delegate_job_safe submit --task test)
|
||||
|
||||
if [ -f "$MAM_LOOP_MARKER" ]; then
|
||||
echo "MARKER: HELD"
|
||||
else
|
||||
echo "MARKER: RELEASED"
|
||||
fi
|
||||
"""
|
||||
res = subprocess.run(["bash", "-c", script], capture_output=True, text=True)
|
||||
assert res.returncode == 0
|
||||
assert "MARKER: HELD" in res.stdout
|
||||
|
||||
|
||||
def test_z9_probe_detects_the_defect(tmp_path):
|
||||
loop_lock_sh = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "loop_lock.sh"
|
||||
skill_dir = tmp_path / ".agents" / "skills" / "multi-agent-mux-delegate-job"
|
||||
skill_dir.mkdir(parents=True)
|
||||
stub_wrapper = skill_dir / "multi-agent-mux-delegate-job"
|
||||
stub_wrapper.write_text("#!/usr/bin/env bash\necho 'JOB_ID: 12345678'\n")
|
||||
stub_wrapper.chmod(0o755)
|
||||
|
||||
defective_func = """
|
||||
delegate_job_safe() {
|
||||
local orig_script="$REPO_ROOT/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job"
|
||||
local tmp_script
|
||||
tmp_script="${orig_script}.${RANDOM}_$$.tmp"
|
||||
cp "$orig_script" "$tmp_script"
|
||||
trap 'rm -f "$tmp_script"' EXIT INT TERM HUP
|
||||
local rc=0
|
||||
bash "$tmp_script" "$@" || rc=$?
|
||||
rm -f "$tmp_script"
|
||||
trap _mam_release_guard EXIT INT TERM HUP
|
||||
return $rc
|
||||
}
|
||||
"""
|
||||
script = f"""#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
REPO_ROOT="{tmp_path}"
|
||||
MAM_LOOP_MARKER="$REPO_ROOT/.mam/loop-guard-active"
|
||||
source "{loop_lock_sh}"
|
||||
|
||||
log_error() {{ echo "ERROR: $@" >&2; }}
|
||||
log_info() {{ echo "INFO: $@" >&2; }}
|
||||
|
||||
_mam_release_guard() {{ mam_release_loop_lock "$MAM_LOOP_MARKER" || true; }}
|
||||
|
||||
mam_acquire_loop_lock "$MAM_LOOP_MARKER"
|
||||
trap _mam_release_guard EXIT INT TERM HUP
|
||||
|
||||
{defective_func}
|
||||
|
||||
PLAN_JOB_OUTPUT=$(delegate_job_safe submit --task test)
|
||||
|
||||
if [ -f "$MAM_LOOP_MARKER" ]; then
|
||||
echo "MARKER: HELD"
|
||||
else
|
||||
echo "MARKER: RELEASED"
|
||||
fi
|
||||
"""
|
||||
res = subprocess.run(["bash", "-c", script], capture_output=True, text=True)
|
||||
assert res.returncode == 0
|
||||
assert "MARKER: RELEASED" in res.stdout
|
||||
|
||||
|
||||
def test_z9_no_tmp_copy_left_in_skill_tree(tmp_path):
|
||||
loop_lock_sh = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "loop_lock.sh"
|
||||
skill_dir = tmp_path / ".agents" / "skills" / "multi-agent-mux-delegate-job"
|
||||
skill_dir.mkdir(parents=True)
|
||||
stub_wrapper = skill_dir / "multi-agent-mux-delegate-job"
|
||||
stub_wrapper.write_text("#!/usr/bin/env bash\necho 'JOB_ID: 12345678'\n")
|
||||
stub_wrapper.chmod(0o755)
|
||||
|
||||
extracted_func = _extract_delegate_job_safe()
|
||||
script = f"""#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
REPO_ROOT="{tmp_path}"
|
||||
MAM_LOOP_MARKER="$REPO_ROOT/.mam/loop-guard-active"
|
||||
source "{loop_lock_sh}"
|
||||
|
||||
log_error() {{ echo "ERROR: $@" >&2; }}
|
||||
log_info() {{ echo "INFO: $@" >&2; }}
|
||||
|
||||
_mam_release_guard() {{ mam_release_loop_lock "$MAM_LOOP_MARKER" || true; }}
|
||||
|
||||
mam_acquire_loop_lock "$MAM_LOOP_MARKER"
|
||||
trap _mam_release_guard EXIT INT TERM HUP
|
||||
|
||||
{extracted_func}
|
||||
|
||||
PLAN_JOB_OUTPUT=$(delegate_job_safe submit --task test)
|
||||
"""
|
||||
res = subprocess.run(["bash", "-c", script], capture_output=True, text=True)
|
||||
assert res.returncode == 0
|
||||
tmp_files = list(skill_dir.glob("*.tmp"))
|
||||
assert tmp_files == []
|
||||
|
||||
|
||||
def test_z9_exit_code_and_diagnostics_propagation(tmp_path):
|
||||
loop_lock_sh = REPO_ROOT / ".agents" / "skills" / "multi-agent-mux-loop" / "scripts" / "loop_lock.sh"
|
||||
skill_dir = tmp_path / ".agents" / "skills" / "multi-agent-mux-delegate-job"
|
||||
skill_dir.mkdir(parents=True)
|
||||
stub_wrapper = skill_dir / "multi-agent-mux-delegate-job"
|
||||
stub_wrapper.write_text("#!/usr/bin/env bash\necho 'syntax error' >&2\nexit 7\n")
|
||||
stub_wrapper.chmod(0o755)
|
||||
|
||||
extracted_func = _extract_delegate_job_safe()
|
||||
script = f"""#!/usr/bin/env bash
|
||||
REPO_ROOT="{tmp_path}"
|
||||
MAM_LOOP_MARKER="$REPO_ROOT/.mam/loop-guard-active"
|
||||
source "{loop_lock_sh}"
|
||||
|
||||
log_error() {{ echo "ERROR: $@" >&2; }}
|
||||
log_info() {{ echo "INFO: $@" >&2; }}
|
||||
|
||||
_mam_release_guard() {{ mam_release_loop_lock "$MAM_LOOP_MARKER" || true; }}
|
||||
|
||||
mam_acquire_loop_lock "$MAM_LOOP_MARKER"
|
||||
trap _mam_release_guard EXIT INT TERM HUP
|
||||
|
||||
{extracted_func}
|
||||
|
||||
rc=0
|
||||
delegate_job_safe submit --task test || rc=$?
|
||||
echo "DELEGATE_RC: $rc"
|
||||
"""
|
||||
res = subprocess.run(["bash", "-c", script], capture_output=True, text=True)
|
||||
assert "DELEGATE_RC: 7" in res.stdout
|
||||
assert "delegate_job_safe failed (exit 7):" in res.stderr
|
||||
assert "bash -n" in res.stderr
|
||||
|
||||
|
||||
# Z-10: Reused PID with stale lstart is NOT blocked
|
||||
|
||||
Reference in New Issue
Block a user