Files
Agent-System-Dev/tests/test_agent_exec.py
T
weichen 8f11d002ae Add recoverable execution agent controller
Why: provide isolated, recoverable per-unit CLI execution and safe host fallback
Evidence: 43 focused tests, configuration validation, governance regression, and diff check
Impact: adds agent-exec validate/run controller, receipts, transports, and Make entrypoint
Refs: U3; docs/superpowers/plans/2026-10-15-project-agent-execution.md
2026-10-15 04:46:25 +00:00

1373 lines
51 KiB
Python

"""End-to-end execution controller contract for agent-exec (U3)."""
from __future__ import annotations
import json
import os
import shutil
import stat
import subprocess
import tempfile
import textwrap
import time
import unittest
import uuid
from pathlib import Path
from unittest import mock
from scripts import agent_exec
def _git(repo: Path, *args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
return subprocess.run(
["git", "-C", str(repo), *args],
check=check,
capture_output=True,
text=True,
)
def _canonical_snapshot(repo: Path) -> dict[str, str]:
head = _git(repo, "rev-parse", "HEAD").stdout.strip()
tree = _git(repo, "rev-parse", "HEAD^{tree}").stdout.strip()
status = _git(repo, "status", "--porcelain=v2", "--branch").stdout
index = _git(repo, "write-tree").stdout.strip()
return {"head": head, "tree": tree, "status": status, "index": index}
def _write_config(
repo: Path,
*,
profiles: dict[str, dict[str, object]],
role_order: list[str],
timeout_seconds: int = 30,
summary_bytes: int = 256,
raw_output_bytes: int = 1024,
) -> None:
profile_lines: list[str] = []
custom_lines: list[str] = []
for name, body in profiles.items():
profile_lines.append(f" {name}:")
profile_lines.append(f" adapter: {body['adapter']}")
profile_lines.append(f" executable: {body['executable']}")
profile_lines.append(f" model: {body.get('model', 'auto')}")
profile_lines.append(f" enabled: {str(body.get('enabled', True)).lower()}")
if body["adapter"] == "custom":
custom_lines.append(f" {name}:")
custom_lines.append(' base_argv: ["${workspace}", "${unit_packet}", "${result_path}"]')
custom_lines.append(" run_everything_argv: []")
custom_lines.append(" workspace_argv: []")
custom_lines.append(" model_argv: []")
custom_lines.append(" prompt_transport: stdin")
custom_lines.append(" output_format: text")
order = ", ".join(role_order)
first = role_order[0]
block = textwrap.dedent(
f"""\
schema: agent-execution/v1
execution:
fresh_process_per_unit: true
permission: run-everything
fallback_to_host: all-unavailable
max_parallel: 1
timeout_seconds: {timeout_seconds}
summary_bytes: {summary_bytes}
raw_output_bytes: {raw_output_bytes}
roles:
implementation: [{order}]
repair: [{first}]
test: [{first}]
review: [{first}]
profiles:
"""
)
block += "\n".join(profile_lines) + "\n"
if custom_lines:
block += "\ncustom_adapters:\n" + "\n".join(custom_lines) + "\n"
(repo / "AGENT-EXECUTION.md").write_text(
"# Fixture\n\n```agent-execution\n" + block + "```\n",
encoding="utf-8",
)
_git(repo, "add", "AGENT-EXECUTION.md")
_git(repo, "commit", "-m", "add agent-execution config")
def _write_unit(
path: Path,
*,
unit_id: str = "u-test",
goal: str = "Create marker file",
allowed_paths: list[str] | None = None,
timeout_seconds: int = 20,
) -> Path:
packet = {
"unit_id": unit_id,
"goal": goal,
"allowed_paths": allowed_paths or ["src/marker.txt"],
"verification": ["test -f src/marker.txt"],
"constraints": ["leave uncommitted"],
"network": False,
"external_cost": False,
"timeout_seconds": timeout_seconds,
}
path.write_text(json.dumps(packet, indent=2) + "\n", encoding="utf-8")
return path
def _write_fake_worker(
bindir: Path,
name: str,
*,
body: str,
help_text: str = "custom worker\n",
) -> Path:
path = bindir / name
path.write_text(
"#!/bin/sh\n"
'if [ "$1" = "--version" ] || [ "$1" = "-v" ] || [ "$1" = "-V" ]; then\n'
' echo "0.0.1-test"\n'
" exit 0\n"
"fi\n"
'if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then\n'
f" cat <<'HELP'\n{help_text}HELP\n"
" exit 0\n"
"fi\n"
+ body,
encoding="utf-8",
)
path.chmod(path.stat().st_mode | stat.S_IEXEC)
return path
class AgentExecControllerTests(unittest.TestCase):
def setUp(self) -> None:
self.tmp = tempfile.TemporaryDirectory(prefix="agent-exec-ctrl-")
self.addCleanup(self.tmp.cleanup)
self.root = Path(self.tmp.name)
self.bindir = self.root / "bin"
self.bindir.mkdir()
self.repo = self.root / "repo"
self.repo.mkdir()
self.receipts = self.root / "receipts"
self.receipts.mkdir()
self._old_path = os.environ.get("PATH", "")
os.environ["PATH"] = f"{self.bindir}{os.pathsep}{self._old_path}"
self.addCleanup(self._restore_path)
self._init_repo()
def _restore_path(self) -> None:
os.environ["PATH"] = self._old_path
def _init_repo(self) -> None:
_git(self.repo, "init")
_git(self.repo, "config", "user.email", "agent-exec@example.com")
_git(self.repo, "config", "user.name", "Agent Exec")
(self.repo / "README.md").write_text("fixture\n", encoding="utf-8")
(self.repo / "src").mkdir()
(self.repo / "src" / ".gitkeep").write_text("", encoding="utf-8")
_git(self.repo, "add", "README.md", "src/.gitkeep")
_git(self.repo, "commit", "-m", "init")
_git(self.repo, "checkout", "-b", "feature/agent-exec-test")
self.base_snap = _canonical_snapshot(self.repo)
def _run_cli(self, *args: str, env: dict[str, str] | None = None) -> subprocess.CompletedProcess[str]:
cmd_env = os.environ.copy()
if env:
cmd_env.update(env)
cmd_env["PYTHONDONTWRITEBYTECODE"] = "1"
workspace = Path(__file__).resolve().parents[1]
existing = cmd_env.get("PYTHONPATH", "")
cmd_env["PYTHONPATH"] = (
f"{workspace}{os.pathsep}{existing}" if existing else str(workspace)
)
return subprocess.run(
["python3", "-m", "scripts.agent_exec", *args],
cwd=str(self.root),
capture_output=True,
text=True,
env=cmd_env,
)
def _assert_canonical_untouched(self) -> None:
self.assertEqual(_canonical_snapshot(self.repo), self.base_snap)
def _configure(self, **kwargs: object) -> None:
_write_config(self.repo, **kwargs) # type: ignore[arg-type]
self.base_snap = _canonical_snapshot(self.repo)
def test_validate_does_not_contact_provider(self) -> None:
marker = self.root / "provider-contacted"
_write_fake_worker(
self.bindir,
"cursor-agent",
body=(
f'echo contacted > "{marker}"\n'
"exit 1\n"
),
help_text=textwrap.dedent(
"""\
Usage: agent [options]
-p, --print
--force
--sandbox
--trust
--workspace
--model
--output-format
"""
),
)
self._configure(
profiles={
"cursor": {
"adapter": "cursor",
"executable": "cursor-agent",
"model": "auto",
}
},
role_order=["cursor"],
)
proc = self._run_cli("validate", "--repo", str(self.repo))
self.assertEqual(proc.returncode, 0, proc.stderr + proc.stdout)
self.assertFalse(marker.exists())
self._assert_canonical_untouched()
def test_run_uses_fresh_process_and_worktree_per_attempt(self) -> None:
meta = self.root / "meta"
meta.mkdir()
_write_fake_worker(
self.bindir,
"worker-a",
body=textwrap.dedent(
f"""\
workspace="$1"
echo "$$" > "{meta}/a.pid"
echo "$workspace" > "{meta}/a.wt"
# fail clean after no writes
exit 7
"""
),
)
_write_fake_worker(
self.bindir,
"worker-b",
body=textwrap.dedent(
f"""\
workspace="$1"
result="$3"
echo "$$" > "{meta}/b.pid"
echo "$workspace" > "{meta}/b.wt"
mkdir -p "$workspace/src"
printf 'ok\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt"],"evidence":["wrote marker"],"scope_expansion":null}}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={
"a": {"adapter": "custom", "executable": "worker-a"},
"b": {"adapter": "custom", "executable": "worker-b"},
},
role_order=["a", "b"],
)
self.base_snap = _canonical_snapshot(self.repo)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "fresh.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["schema"], "agent-exec-receipt/v1")
self.assertEqual(data["terminal_status"], "success")
a_pid = (meta / "a.pid").read_text(encoding="utf-8").strip()
b_pid = (meta / "b.pid").read_text(encoding="utf-8").strip()
a_wt = (meta / "a.wt").read_text(encoding="utf-8").strip()
b_wt = (meta / "b.wt").read_text(encoding="utf-8").strip()
self.assertNotEqual(a_pid, b_pid)
self.assertNotEqual(a_wt, b_wt)
self.assertTrue(a_wt.startswith(f"/tmp/agent-exec-{os.getuid()}/"))
self.assertTrue(b_wt.startswith(f"/tmp/agent-exec-{os.getuid()}/"))
self._assert_canonical_untouched()
def test_missing_profiles_fall_through_in_declared_order(self) -> None:
log = self.root / "order.log"
log.write_text("", encoding="utf-8")
_write_fake_worker(
self.bindir,
"second",
body=textwrap.dedent(
f"""\
echo second >> "{log}"
workspace="$1"
result="$3"
mkdir -p "$workspace/src"
printf 'ok\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt"],"evidence":[],"scope_expansion":null}}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={
"missing": {"adapter": "custom", "executable": "does-not-exist-xyz"},
"second": {"adapter": "custom", "executable": "second"},
},
role_order=["missing", "second"],
)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "order.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
names = [a["profile"] for a in data["attempts"]]
self.assertEqual(names, ["missing", "second"])
self.assertEqual(data["attempts"][0]["terminal_status"], "unavailable")
self.assertEqual(log.read_text(encoding="utf-8").strip(), "second")
self._assert_canonical_untouched()
def test_all_unavailable_authorizes_host_fallback_once(self) -> None:
self._configure(
profiles={
"m1": {"adapter": "custom", "executable": "gone-one"},
"m2": {"adapter": "custom", "executable": "gone-two"},
},
role_order=["m1", "m2"],
)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "fallback.json"
before = list(self.repo.rglob("*"))
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(
proc.returncode, agent_exec.EXIT_HOST_FALLBACK, proc.stderr + proc.stdout
)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "host-fallback-authorized")
self.assertEqual(len(data["attempts"]), 2)
self.assertTrue(all(a["terminal_status"] == "unavailable" for a in data["attempts"]))
self.assertEqual(data.get("host_fallback_authorizations"), 1)
# Controller must not implement the unit itself.
after = list(self.repo.rglob("*"))
self.assertEqual(before, after)
self.assertFalse((self.repo / "src" / "marker.txt").exists())
self.assertIn("allowed_paths", data["host_scope"])
self._assert_canonical_untouched()
def test_failed_clean_can_continue_after_exact_restore(self) -> None:
_write_fake_worker(
self.bindir,
"dirty",
body=textwrap.dedent(
"""\
workspace="$1"
mkdir -p "$workspace/src"
printf 'tmp\\n' > "$workspace/src/marker.txt"
exit 9
"""
),
)
_write_fake_worker(
self.bindir,
"ok",
body=textwrap.dedent(
"""\
workspace="$1"
result="$3"
mkdir -p "$workspace/src"
printf 'final\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt"],"evidence":[],"scope_expansion":null}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={
"dirty": {"adapter": "custom", "executable": "dirty"},
"ok": {"adapter": "custom", "executable": "ok"},
},
role_order=["dirty", "ok"],
)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "clean.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["attempts"][0]["terminal_status"], "failed-clean")
self.assertTrue(data["attempts"][0]["cleanup"]["exact_restore"])
self.assertEqual(data["terminal_status"], "success")
self._assert_canonical_untouched()
def test_failed_unknown_blocks_fallback(self) -> None:
launched = self.root / "launched.log"
launched.write_text("", encoding="utf-8")
_write_fake_worker(
self.bindir,
"unknown",
body=textwrap.dedent(
"""\
workspace="$1"
mkdir -p "$workspace/src"
printf 'x\\n' > "$workspace/src/marker.txt"
# Abnormal termination / disconnect: cannot prove absence of side effects.
kill -KILL $$
"""
),
)
_write_fake_worker(
self.bindir,
"next",
body=textwrap.dedent(
f"""\
echo next >> "{launched}"
exit 0
"""
),
)
self._configure(
profiles={
"unknown": {"adapter": "custom", "executable": "unknown"},
"next": {"adapter": "custom", "executable": "next"},
},
role_order=["unknown", "next"],
)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "unknown.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_BLOCKED, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "failed-unknown")
self.assertEqual(len(data["attempts"]), 1)
self.assertEqual(launched.read_text(encoding="utf-8"), "")
self.assertNotEqual(data["terminal_status"], "host-fallback-authorized")
self._assert_canonical_untouched()
def test_scope_expansion_preserves_workspace_and_blocks(self) -> None:
launched = self.root / "scope-next.log"
launched.write_text("", encoding="utf-8")
_write_fake_worker(
self.bindir,
"scope",
body=textwrap.dedent(
"""\
workspace="$1"
result="$3"
printf 'bad\\n' > "$workspace/README.md"
printf '%s\\n' '{"terminal_status":"completed","summary":"expanded","changed_files":["README.md"],"evidence":[],"scope_expansion":null}' > "$result"
exit 0
"""
),
)
_write_fake_worker(
self.bindir,
"next",
body=textwrap.dedent(
f"""\
echo next >> "{launched}"
exit 0
"""
),
)
self._configure(
profiles={
"scope": {"adapter": "custom", "executable": "scope"},
"next": {"adapter": "custom", "executable": "next"},
},
role_order=["scope", "next"],
)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "scope.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(
proc.returncode, agent_exec.EXIT_SCOPE_EXPANSION, proc.stderr + proc.stdout
)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "scope-expansion")
self.assertEqual(len(data["attempts"]), 1)
self.assertEqual(launched.read_text(encoding="utf-8"), "")
attempt = data["attempts"][0]
self.assertTrue(Path(attempt["worktree"]).exists())
self.assertTrue(attempt.get("workspace_preserved"))
self._assert_canonical_untouched()
def test_worker_commit_or_head_move_is_rejected(self) -> None:
"""Worker HEAD/commit, staged index, and config/ref mutations are fail-stop."""
cases = {
"commit": textwrap.dedent(
"""\
workspace="$1"
mkdir -p "$workspace/src"
printf 'c\\n' > "$workspace/src/marker.txt"
git -C "$workspace" add src/marker.txt
git -C "$workspace" -c user.email=t@e -c user.name=t commit -m 'worker commit'
exit 0
"""
),
"staged": textwrap.dedent(
"""\
workspace="$1"
mkdir -p "$workspace/src"
printf 'staged\\n' > "$workspace/src/marker.txt"
git -C "$workspace" add src/marker.txt
exit 0
"""
),
"config_ref": textwrap.dedent(
"""\
workspace="$1"
git -C "$workspace" config --local agentexec.probe 1
git -C "$workspace" update-ref refs/agent-exec-probe/tmp HEAD
exit 0
"""
),
}
for label, body in cases.items():
with self.subTest(case=label):
launched = self.root / f"commit-next-{label}.log"
launched.write_text("", encoding="utf-8")
worker = f"committer-{label}"
_write_fake_worker(self.bindir, worker, body=body)
_write_fake_worker(
self.bindir,
f"next-{label}",
body=textwrap.dedent(
f"""\
echo next >> "{launched}"
exit 0
"""
),
)
self._configure(
profiles={
"committer": {"adapter": "custom", "executable": worker},
"next": {
"adapter": "custom",
"executable": f"next-{label}",
},
},
role_order=["committer", "next"],
)
self.base_snap = _canonical_snapshot(self.repo)
unit = _write_unit(self.root / f"unit-{label}.json")
receipt = self.receipts / f"commit-{label}.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(
proc.returncode,
agent_exec.EXIT_BLOCKED,
proc.stderr + proc.stdout,
)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "failed-unknown")
self.assertEqual(len(data["attempts"]), 1)
self.assertEqual(launched.read_text(encoding="utf-8"), "")
self.assertTrue(data["attempts"][0].get("workspace_preserved"))
self._assert_canonical_untouched()
def test_untracked_and_binary_files_enter_transport_inventory(self) -> None:
_write_fake_worker(
self.bindir,
"binwriter",
body=textwrap.dedent(
"""\
workspace="$1"
result="$3"
mkdir -p "$workspace/src"
printf 'text\\n' > "$workspace/src/marker.txt"
printf 'bin\\0data' > "$workspace/src/blob.bin"
printf '%s\\n' '{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt","src/blob.bin"],"evidence":[],"scope_expansion":null}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={"binwriter": {"adapter": "custom", "executable": "binwriter"}},
role_order=["binwriter"],
)
unit = _write_unit(
self.root / "unit.json",
allowed_paths=["src/marker.txt", "src/blob.bin"],
)
receipt = self.receipts / "binary.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
changed = set(data["changed_paths"])
self.assertIn("src/marker.txt", changed)
self.assertIn("src/blob.bin", changed)
transport = data["transport"]
self.assertTrue(transport["ref"].startswith("refs/agent-exec/"))
self._assert_canonical_untouched()
def test_timeout_kills_process_group_and_records_receipt(self) -> None:
child_pid_file = self.root / "child.pid"
_write_fake_worker(
self.bindir,
"sleeper",
body=textwrap.dedent(
f"""\
# Start a child in the same process group
(sleep 120 & echo $! > "{child_pid_file}"; wait)
"""
),
)
self._configure(
profiles={"sleeper": {"adapter": "custom", "executable": "sleeper"}},
role_order=["sleeper"],
timeout_seconds=2,
)
unit = _write_unit(self.root / "unit.json", timeout_seconds=2)
receipt = self.receipts / "timeout.json"
t0 = time.time()
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
elapsed = time.time() - t0
self.assertLess(elapsed, 15)
self.assertEqual(proc.returncode, agent_exec.EXIT_BLOCKED, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "failed-unknown")
self.assertTrue(data["attempts"][0].get("timed_out"))
if child_pid_file.exists():
child = int(child_pid_file.read_text(encoding="utf-8").strip())
# Child must not still be alive.
still = subprocess.run(
["kill", "-0", str(child)],
check=False,
capture_output=True,
)
self.assertNotEqual(still.returncode, 0)
self._assert_canonical_untouched()
def test_summary_and_raw_log_caps_are_enforced(self) -> None:
_write_fake_worker(
self.bindir,
"noisy",
body=textwrap.dedent(
"""\
workspace="$1"
result="$3"
python3 - <<'PY'
import sys
chunk = b"A" * (512 * 1024)
sys.stdout.buffer.write(chunk)
sys.stdout.buffer.flush()
sys.stderr.buffer.write(b"B" * (512 * 1024))
sys.stderr.buffer.flush()
PY
mkdir -p "$workspace/src"
printf 'ok\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{"terminal_status":"completed","summary":"'"$(python3 -c 'print("S"*2000)')"'","changed_files":["src/marker.txt"],"evidence":[],"scope_expansion":null}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={"noisy": {"adapter": "custom", "executable": "noisy"}},
role_order=["noisy"],
summary_bytes=64,
raw_output_bytes=128,
)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "caps.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
summary = data["summary"]
self.assertLessEqual(len(summary.encode("utf-8")), 64)
attempt = data["attempts"][0]
raw_path = Path(attempt["raw_log"])
self.assertTrue(raw_path.is_file())
self.assertLessEqual(raw_path.stat().st_size, 128)
cleanup = attempt.get("cleanup") or {}
self.assertLessEqual(
int(cleanup.get("stdout_tail_bytes", agent_exec.OUTPUT_TAIL_BYTES)),
agent_exec.OUTPUT_TAIL_BYTES,
)
self.assertLessEqual(
int(cleanup.get("stderr_tail_bytes", agent_exec.OUTPUT_TAIL_BYTES)),
agent_exec.OUTPUT_TAIL_BYTES,
)
self._assert_canonical_untouched()
def test_receipt_redacts_environment_secrets(self) -> None:
secret = "sk-testdeadbeefdeadbeef123456"
_write_fake_worker(
self.bindir,
"leaky",
body=textwrap.dedent(
"""\
workspace="$1"
result="$3"
echo "token=$AGENT_EXEC_TEST_SECRET"
mkdir -p "$workspace/src"
printf 'ok\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt"],"evidence":[],"scope_expansion":null}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={"leaky": {"adapter": "custom", "executable": "leaky"}},
role_order=["leaky"],
)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "redact.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
env={"AGENT_EXEC_TEST_SECRET": secret},
)
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
text = receipt.read_text(encoding="utf-8")
self.assertNotIn(secret, text)
data = json.loads(text)
self.assertIn("***", json.dumps(data.get("attempts", [{}])[0].get("summary", "***")))
self._assert_canonical_untouched()
def test_success_creates_base_parented_transport_without_touching_canonical_head(
self,
) -> None:
_write_fake_worker(
self.bindir,
"ok",
body=textwrap.dedent(
"""\
workspace="$1"
result="$3"
mkdir -p "$workspace/src"
printf 'shipped\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt"],"evidence":[],"scope_expansion":null}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={"ok": {"adapter": "custom", "executable": "ok"}},
role_order=["ok"],
)
unit = _write_unit(self.root / "unit.json")
receipt = self.receipts / "transport.json"
before = _canonical_snapshot(self.repo)
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
after = _canonical_snapshot(self.repo)
self.assertEqual(before, after)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "success")
transport = data["transport"]
ref = transport["ref"]
self.assertRegex(ref, r"^refs/agent-exec/[^/]+/[^/]+$")
parent = _git(self.repo, "rev-parse", f"{ref}^").stdout.strip()
self.assertEqual(parent, before["head"])
commit = _git(self.repo, "rev-parse", ref).stdout.strip()
self.assertNotEqual(commit, before["head"])
# Canonical branch HEAD unchanged
branch_head = _git(self.repo, "rev-parse", "HEAD").stdout.strip()
self.assertEqual(branch_head, before["head"])
attempt = data["attempts"][0]
self.assertTrue(attempt.get("workspace_preserved"))
cleanup = attempt.get("cleanup") or {}
self.assertFalse(cleanup.get("exact_restore"))
self.assertTrue(cleanup.get("transport_created"))
def test_canonical_checkout_mutation_is_failed_unknown(self) -> None:
"""Worker mutating canonical files/index while keeping worktree clean is blocked."""
launched = self.root / "canon-next.log"
launched.write_text("", encoding="utf-8")
# Resolve the main checkout from the linked worktree .git pointer.
_write_fake_worker(
self.bindir,
"canonmut",
body=textwrap.dedent(
"""\
workspace="$1"
common=$(git -C "$workspace" rev-parse --path-format=absolute --git-common-dir)
canonical=$(dirname "$common")
printf 'canonical-mutated\\n' >> "$canonical/README.md"
git -C "$canonical" add README.md
# Detached worktree left clean on purpose.
exit 0
"""
),
)
_write_fake_worker(
self.bindir,
"canonnext",
body=textwrap.dedent(
f"""\
echo next >> "{launched}"
exit 0
"""
),
)
self._configure(
profiles={
"canonmut": {"adapter": "custom", "executable": "canonmut"},
"canonnext": {"adapter": "custom", "executable": "canonnext"},
},
role_order=["canonmut", "canonnext"],
)
unit = _write_unit(self.root / "unit-canon.json")
receipt = self.receipts / "canon.json"
before = _canonical_snapshot(self.repo)
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_BLOCKED, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "failed-unknown")
self.assertEqual(len(data["attempts"]), 1)
attempt = data["attempts"][0]
self.assertTrue(attempt.get("workspace_preserved"))
self.assertIn("canonical", (attempt.get("reason") or "").lower())
self.assertEqual(launched.read_text(encoding="utf-8"), "")
self.assertEqual(data.get("host_fallback_authorizations", 0), 0)
# Mutation is retained for reconciliation; canonical is intentionally dirty.
after = _canonical_snapshot(self.repo)
self.assertNotEqual(after["status"], before["status"])
def test_corrupt_worktree_git_pointer_yields_failed_unknown_receipt(self) -> None:
"""Corrupted worktree .git pointer must produce a stable blocked receipt."""
_write_fake_worker(
self.bindir,
"corruptgit",
body=textwrap.dedent(
"""\
workspace="$1"
printf 'not-a-valid-gitdir\\n' > "$workspace/.git"
exit 0
"""
),
)
self._configure(
profiles={"corruptgit": {"adapter": "custom", "executable": "corruptgit"}},
role_order=["corruptgit"],
)
unit = _write_unit(self.root / "unit-corrupt.json")
receipt = self.receipts / "corrupt.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_BLOCKED, proc.stderr + proc.stdout)
self.assertTrue(receipt.is_file())
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "failed-unknown")
self.assertEqual(len(data["attempts"]), 1)
attempt = data["attempts"][0]
self.assertEqual(attempt["terminal_status"], "failed-unknown")
self.assertTrue(attempt.get("workspace_preserved"))
self.assertFalse((attempt.get("cleanup") or {}).get("exact_restore"))
reason = (attempt.get("reason") or "").lower()
self.assertTrue(
"inspection" in reason or "pointer" in reason or ".git" in reason,
attempt.get("reason"),
)
self.assertNotIn("Traceback", proc.stderr)
self._assert_canonical_untouched()
def test_networked_nonzero_no_file_change_is_failed_unknown(self) -> None:
"""network=true nonzero exit with no file changes cannot be failed-clean."""
launched = self.root / "net-next.log"
launched.write_text("", encoding="utf-8")
_write_fake_worker(
self.bindir,
"netfail",
body=textwrap.dedent(
"""\
# No worktree writes; nonzero exit after possible external contact.
exit 11
"""
),
)
_write_fake_worker(
self.bindir,
"netnext",
body=textwrap.dedent(
f"""\
echo next >> "{launched}"
exit 0
"""
),
)
self._configure(
profiles={
"netfail": {"adapter": "custom", "executable": "netfail"},
"netnext": {"adapter": "custom", "executable": "netnext"},
},
role_order=["netfail", "netnext"],
)
unit = _write_unit(self.root / "unit-net.json")
packet = json.loads(unit.read_text(encoding="utf-8"))
packet["network"] = True
packet["external_cost"] = False
unit.write_text(json.dumps(packet, indent=2) + "\n", encoding="utf-8")
receipt = self.receipts / "netfail.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_BLOCKED, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "failed-unknown")
self.assertEqual(len(data["attempts"]), 1)
self.assertEqual(data["attempts"][0]["terminal_status"], "failed-unknown")
self.assertEqual(launched.read_text(encoding="utf-8"), "")
self.assertNotEqual(data["terminal_status"], "host-fallback-authorized")
self.assertEqual(data.get("host_fallback_authorizations", 0), 0)
self._assert_canonical_untouched()
def test_git_admin_staged_config_ref_mutation_is_failed_unknown(self) -> None:
"""Staged index plus local config/ref mutation preserves workspace and stops."""
launched = self.root / "admin-next.log"
launched.write_text("", encoding="utf-8")
_write_fake_worker(
self.bindir,
"adminmut",
body=textwrap.dedent(
"""\
workspace="$1"
mkdir -p "$workspace/src"
printf 'idx\\n' > "$workspace/src/marker.txt"
git -C "$workspace" add src/marker.txt
git -C "$workspace" config --local agentexec.mutation yes
git -C "$workspace" update-ref refs/temporary/agent-exec-admin HEAD
exit 0
"""
),
)
_write_fake_worker(
self.bindir,
"adminnext",
body=textwrap.dedent(
f"""\
echo next >> "{launched}"
exit 0
"""
),
)
self._configure(
profiles={
"adminmut": {"adapter": "custom", "executable": "adminmut"},
"adminnext": {"adapter": "custom", "executable": "adminnext"},
},
role_order=["adminmut", "adminnext"],
)
unit = _write_unit(self.root / "unit-admin.json")
receipt = self.receipts / "admin.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertEqual(proc.returncode, agent_exec.EXIT_BLOCKED, proc.stderr + proc.stdout)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "failed-unknown")
self.assertEqual(len(data["attempts"]), 1)
attempt = data["attempts"][0]
self.assertTrue(attempt.get("workspace_preserved"))
inventory = attempt.get("inventory_paths") or attempt.get("changed_paths")
self.assertIn("src/marker.txt", inventory)
self.assertEqual(launched.read_text(encoding="utf-8"), "")
self.assertEqual(data.get("host_fallback_authorizations", 0), 0)
self._assert_canonical_untouched()
def test_symlink_allowed_path_escape_rejects_without_external_write(self) -> None:
"""Allowed path through symlink escaping the worktree cannot touch outside."""
outside = self.root / "outside-target"
outside.mkdir()
victim = outside / "victim.txt"
victim.write_text("untouched\n", encoding="utf-8")
link = self.repo / "src" / "escape"
link.symlink_to(outside, target_is_directory=True)
_git(self.repo, "add", "src/escape")
_git(self.repo, "commit", "-m", "add escape symlink")
self.base_snap = _canonical_snapshot(self.repo)
launched = self.root / "symlink-next.log"
launched.write_text("", encoding="utf-8")
_write_fake_worker(
self.bindir,
"symworker",
body=textwrap.dedent(
"""\
workspace="$1"
# If launch were allowed, attempt to write through the symlink.
printf 'pwned\\n' > "$workspace/src/escape/victim.txt"
exit 0
"""
),
)
_write_fake_worker(
self.bindir,
"symnext",
body=textwrap.dedent(
f"""\
echo next >> "{launched}"
exit 0
"""
),
)
self._configure(
profiles={
"symworker": {"adapter": "custom", "executable": "symworker"},
"symnext": {"adapter": "custom", "executable": "symnext"},
},
role_order=["symworker", "symnext"],
)
unit = _write_unit(
self.root / "unit-symlink.json",
allowed_paths=["src/escape/victim.txt"],
)
receipt = self.receipts / "symlink.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
self.assertNotEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
self.assertNotEqual(proc.returncode, agent_exec.EXIT_HOST_FALLBACK)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertNotEqual(data["terminal_status"], "success")
self.assertNotEqual(data["terminal_status"], "host-fallback-authorized")
self.assertEqual(victim.read_text(encoding="utf-8"), "untouched\n")
self.assertEqual(launched.read_text(encoding="utf-8"), "")
self._assert_canonical_untouched()
def test_secret_crossing_raw_log_flush_boundary_is_redacted(self) -> None:
"""Exact env secrets must not leak when split across raw-log drain/flush edges."""
secret = "SECRETBOUNDARYTOKEN"
_write_fake_worker(
self.bindir,
"boundary",
body=textwrap.dedent(
f"""\
workspace="$1"
result="$3"
python3 - <<'PY'
import os, sys
secret = os.environ["AGENT_EXEC_TEST_SECRET"]
sys.stdout.buffer.write(b"A" * 120 + secret.encode() + b"Z" * 60)
sys.stdout.buffer.flush()
PY
mkdir -p "$workspace/src"
printf 'ok\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt"],"evidence":[],"scope_expansion":null}}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={"boundary": {"adapter": "custom", "executable": "boundary"}},
role_order=["boundary"],
raw_output_bytes=4096,
)
unit = _write_unit(self.root / "unit-boundary.json")
receipt = self.receipts / "boundary-redact.json"
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
env={"AGENT_EXEC_TEST_SECRET": secret},
)
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS, proc.stderr + proc.stdout)
receipt_text = receipt.read_text(encoding="utf-8")
self.assertNotIn(secret, receipt_text)
data = json.loads(receipt_text)
raw_path = Path(data["attempts"][0]["raw_log"])
raw_bytes = raw_path.read_bytes()
self.assertNotIn(secret.encode("utf-8"), raw_bytes)
self.assertNotIn(secret, raw_bytes.decode("utf-8", "replace"))
self._assert_canonical_untouched()
def test_large_stdin_with_early_stdout_does_not_deadlock(self) -> None:
"""Stdin must be select-driven so early stdout beyond pipe capacity cannot hang."""
# Stay under adapter MAX_UNIT_PACKET_BYTES (262144) while exceeding pipe capacity.
pad = "P" * (128 * 1024)
_write_fake_worker(
self.bindir,
"pipefill",
body=textwrap.dedent(
"""\
workspace="$1"
result="$3"
# Use -c so process stdin (unit packet) remains readable by Python.
python3 -c 'import sys; sys.stdout.buffer.write(b"X"*(256*1024)); sys.stdout.buffer.flush(); sys.stdin.buffer.read()'
mkdir -p "$workspace/src"
printf 'ok\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt"],"evidence":[],"scope_expansion":null}' > "$result"
exit 0
"""
),
)
self._configure(
profiles={"pipefill": {"adapter": "custom", "executable": "pipefill"}},
role_order=["pipefill"],
timeout_seconds=15,
raw_output_bytes=1024,
)
unit = _write_unit(
self.root / "unit-pipefill.json",
timeout_seconds=15,
)
# Inflate the stdin-delivered unit packet well beyond pipe capacity.
packet = json.loads(unit.read_text(encoding="utf-8"))
packet["constraints"] = ["leave uncommitted", pad]
unit.write_text(json.dumps(packet) + "\n", encoding="utf-8")
self.assertGreater(unit.stat().st_size, 65536)
self.assertLessEqual(unit.stat().st_size, 262144)
receipt = self.receipts / "pipefill.json"
started = time.monotonic()
proc = self._run_cli(
"run",
"--repo",
str(self.repo),
"--role",
"implementation",
"--unit",
str(unit),
"--receipt",
str(receipt),
)
elapsed = time.monotonic() - started
self.assertLessEqual(elapsed, 20.0, f"controller hung for {elapsed:.1f}s")
self.assertIn(
proc.returncode,
{
agent_exec.EXIT_SUCCESS,
agent_exec.EXIT_BLOCKED,
},
proc.stderr + proc.stdout,
)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertIn(
data["terminal_status"],
{"success", "failed-unknown", "blocked"},
)
if data["terminal_status"] == "success":
self.assertEqual(proc.returncode, agent_exec.EXIT_SUCCESS)
self._assert_canonical_untouched()
def test_transport_construction_failure_is_failed_unknown(self) -> None:
"""Host transport staging/commit-tree/update-ref failure stays recoverable."""
launched = self.root / "transport-next.log"
launched.write_text("", encoding="utf-8")
_write_fake_worker(
self.bindir,
"txok",
body=textwrap.dedent(
"""\
workspace="$1"
result="$3"
mkdir -p "$workspace/src"
printf 'ok\\n' > "$workspace/src/marker.txt"
printf '%s\\n' '{"terminal_status":"completed","summary":"done","changed_files":["src/marker.txt"],"evidence":[],"scope_expansion":null}' > "$result"
exit 0
"""
),
)
_write_fake_worker(
self.bindir,
"txnext",
body=textwrap.dedent(
f"""\
echo next >> "{launched}"
exit 0
"""
),
)
self._configure(
profiles={
"txok": {"adapter": "custom", "executable": "txok"},
"txnext": {"adapter": "custom", "executable": "txnext"},
},
role_order=["txok", "txnext"],
)
unit = _write_unit(self.root / "unit-txfail.json")
receipt = self.receipts / "txfail.json"
with mock.patch.object(
agent_exec,
"_create_transport_commit",
side_effect=subprocess.CalledProcessError(
1, ["git", "commit-tree"], stderr="simulated transport failure"
),
):
code = agent_exec.cmd_run(
self.repo,
role="implementation",
unit_path=unit,
receipt_path=receipt,
)
self.assertEqual(code, agent_exec.EXIT_BLOCKED)
data = json.loads(receipt.read_text(encoding="utf-8"))
self.assertEqual(data["terminal_status"], "failed-unknown")
self.assertEqual(len(data["attempts"]), 1)
attempt = data["attempts"][0]
self.assertEqual(attempt["terminal_status"], "failed-unknown")
self.assertTrue(attempt.get("workspace_preserved"))
cleanup = attempt.get("cleanup") or {}
self.assertFalse(cleanup.get("transport_created", True))
self.assertIn("transport construction failed", attempt.get("reason") or data.get("reason") or "")
self.assertEqual(data.get("host_fallback_authorizations", 0), 0)
self.assertEqual(launched.read_text(encoding="utf-8"), "")
self._assert_canonical_untouched()
if __name__ == "__main__":
unittest.main()