diff --git a/README.md b/README.md index 8c7c8fe..fa3b98b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Façade repo for Prophet command surface and SourceOS / AgentPlane bootstrap del | `prophet sourceos office ...` | `sourceosctl office ...` | `SourceOS-Linux/sourceos-devtools` | | `prophet sourceos agent-term ...` | `agent-term ...` | `SourceOS-Linux/agent-term` | | `prophet agentplane ...` | `sp-run ...` | `SocioProphet/agentplane` | +| `prophet governed-runner ...` | `sp-run ...` | `SocioProphet/agentplane` | ## Examples @@ -40,6 +41,11 @@ prophet agentplane preflight ./governed-run-contract.json prophet agentplane admit ./governed-run-contract.json --preflight ./preflight-receipt.json --authority-state ./agent-authority-current-state.json --projected-cost-usd 0.25 prophet agentplane dossier ./.socioprophet/runs/governed-run-alpha-001 prophet agentplane validate-dossier ./run-dossier.json +prophet governed-runner doctor +prophet governed-runner preflight ./governed-run-contract.json +prophet governed-runner admit ./governed-run-contract.json --preflight ./preflight-receipt.json --authority-state ./agent-authority-current-state.json --projected-cost-usd 0.25 +prophet governed-runner dossier ./.socioprophet/runs/governed-run-alpha-001 +prophet governed-runner validate-dossier ./run-dossier.json ``` ## Install path @@ -61,6 +67,7 @@ For AgentPlane governed-runner commands, install or expose the AgentPlane-owned python3 -m pip install -e /path/to/agentplane sp-run doctor prophet agentplane doctor +prophet governed-runner doctor ``` ## Boundary @@ -84,4 +91,4 @@ This repo does not own: Those remain in their owning repositories. -`prophet agentplane ...` is a facade over `sp-run ...`; the implementation remains in `SocioProphet/agentplane`. +`prophet agentplane ...` and `prophet governed-runner ...` are facades over `sp-run ...`; the implementation remains in `SocioProphet/agentplane`. diff --git a/src/prophet_cli/cli.py b/src/prophet_cli/cli.py index 6edee7a..a4197eb 100644 --- a/src/prophet_cli/cli.py +++ b/src/prophet_cli/cli.py @@ -33,6 +33,11 @@ def _delegate(binary: str, args: Sequence[str]) -> int: return int(completed.returncode) +def _add_sp_run_delegate(sub: argparse._SubParsersAction, name: str, help_text: str) -> None: + parser = sub.add_parser(name, help=help_text) + parser.add_argument("args", nargs=argparse.REMAINDER, help="Arguments passed to sp-run") + + def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="prophet", @@ -67,8 +72,8 @@ def build_parser() -> argparse.ArgumentParser: agent_term = sourceos_sub.add_parser("agent-term", help="Delegate AgentTerm commands") agent_term.add_argument("args", nargs=argparse.REMAINDER, help="Arguments passed to agent-term") - agentplane = sub.add_parser("agentplane", help="Delegate AgentPlane governed-runner commands") - agentplane.add_argument("args", nargs=argparse.REMAINDER, help="Arguments passed to sp-run") + _add_sp_run_delegate(sub, "agentplane", "Delegate AgentPlane governed-runner commands") + _add_sp_run_delegate(sub, "governed-runner", "Delegate governed-runner commands") return parser @@ -91,7 +96,7 @@ def main(argv: list[str] | None = None) -> int: if args.sourceos_command == "agent-term": return _delegate("agent-term", list(args.args)) - if args.command == "agentplane": + if args.command in {"agentplane", "governed-runner"}: return _delegate("sp-run", list(args.args)) parser.error("unknown command") @@ -99,4 +104,4 @@ def main(argv: list[str] | None = None) -> int: if __name__ == "__main__": - raise SystemExit(main(sys.argv[1:])) \ No newline at end of file + raise SystemExit(main(sys.argv[1:])) diff --git a/tests/test_cli.py b/tests/test_cli.py index 666d577..d31c869 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -188,6 +188,60 @@ def fake_run(cmd, check=False): ]] +def test_governed_runner_alias_delegates_to_sp_run(monkeypatch): + calls: list[list[str]] = [] + + monkeypatch.setattr("shutil.which", lambda binary: f"/usr/bin/{binary}") + + def fake_run(cmd, check=False): + calls.append(cmd) + assert check is False + return Completed(0) + + monkeypatch.setattr(subprocess, "run", fake_run) + + rc = main(["governed-runner", "doctor"]) + + assert rc == 0 + assert calls == [["/usr/bin/sp-run", "doctor"]] + + +def test_governed_runner_dossier_delegates_to_sp_run(monkeypatch): + calls: list[list[str]] = [] + + monkeypatch.setattr("shutil.which", lambda binary: f"/usr/bin/{binary}") + + def fake_run(cmd, check=False): + calls.append(cmd) + assert check is False + return Completed(0) + + monkeypatch.setattr(subprocess, "run", fake_run) + + rc = main(["governed-runner", "dossier", ".socioprophet/runs/demo"]) + + assert rc == 0 + assert calls == [["/usr/bin/sp-run", "dossier", ".socioprophet/runs/demo"]] + + +def test_governed_runner_validate_dossier_delegates_to_sp_run(monkeypatch): + calls: list[list[str]] = [] + + monkeypatch.setattr("shutil.which", lambda binary: f"/usr/bin/{binary}") + + def fake_run(cmd, check=False): + calls.append(cmd) + assert check is False + return Completed(0) + + monkeypatch.setattr(subprocess, "run", fake_run) + + rc = main(["governed-runner", "validate-dossier", "run-dossier.json"]) + + assert rc == 0 + assert calls == [["/usr/bin/sp-run", "validate-dossier", "run-dossier.json"]] + + def test_missing_delegate_returns_127(monkeypatch, capsys): monkeypatch.setattr("shutil.which", lambda binary: None) @@ -207,4 +261,15 @@ def test_missing_sp_run_delegate_returns_127(monkeypatch, capsys): captured = capsys.readouterr() assert rc == 127 assert "required delegate not found: sp-run" in captured.err - assert "AgentPlane" in captured.err \ No newline at end of file + assert "AgentPlane" in captured.err + + +def test_missing_sp_run_for_governed_runner_alias_returns_127(monkeypatch, capsys): + monkeypatch.setattr("shutil.which", lambda binary: None) + + rc = main(["governed-runner", "doctor"]) + + captured = capsys.readouterr() + assert rc == 127 + assert "required delegate not found: sp-run" in captured.err + assert "AgentPlane" in captured.err