diff --git a/tests/hermes_cli/test_kanban_cli_exit_status.py b/tests/hermes_cli/test_kanban_cli_exit_status.py new file mode 100644 index 0000000000000..79be890e91a3c --- /dev/null +++ b/tests/hermes_cli/test_kanban_cli_exit_status.py @@ -0,0 +1,60 @@ +"""Regression coverage for Kanban CLI process exit status propagation.""" + +from __future__ import annotations + +import json +import os +import subprocess +import sys +from pathlib import Path + + +ROOT = Path(__file__).parents[2] + + +def _run_hermes(home: Path, *args: str, marker: bool = False) -> subprocess.CompletedProcess[str]: + env = os.environ.copy() + env["HERMES_HOME"] = str(home) + env["HERMES_KANBAN_HOME"] = str(home) + for name in ( + "HERMES_KANBAN_BOARD", + "HERMES_KANBAN_DB", + "HERMES_KANBAN_WORKSPACES_ROOT", + ): + env.pop(name, None) + env["PYTHONPATH"] = str(ROOT) + os.pathsep + env.get("PYTHONPATH", "") + if marker: + env["HERMES_DELEGATED_CHILD_CONTEXT"] = "1" + else: + env.pop("HERMES_DELEGATED_CHILD_CONTEXT", None) + return subprocess.run( + [sys.executable, "-m", "hermes_cli.main", *args], + cwd=ROOT, + env=env, + capture_output=True, + text=True, + check=False, + timeout=30, + ) + + +def test_delegated_child_kanban_cli_refusal_returns_nonzero_exit_status(tmp_path): + """A printed Kanban mutation refusal must not look like CLI success.""" + home = tmp_path / "hermes" + home.mkdir() + + created = _run_hermes(home, "kanban", "create", "exit status probe", "--json") + assert created.returncode == 0, created.stderr + task_id = json.loads(created.stdout)["id"] + + refused = _run_hermes( + home, + "kanban", + "comment", + task_id, + "must be refused", + marker=True, + ) + + assert refused.returncode == 1 + assert "delegate_task child contexts cannot mutate Kanban tasks via the CLI" in refused.stderr