diff --git a/loopx/cli.py b/loopx/cli.py index c346f98989..efc48c1904 100644 --- a/loopx/cli.py +++ b/loopx/cli.py @@ -176,10 +176,7 @@ handle_shared_goal_alignment_command, register_shared_goal_alignment_command, ) -from .cli_commands.goal_acceptance import ( - handle_goal_acceptance_command, - register_goal_acceptance_command, -) +from .cli_commands.goal_acceptance import register_goal_acceptance_command from .cli_commands.goal_direction import register_goal_direction, handle_goal_direction from .cli_commands.goal_amendment_proposal import ( handle_goal_amendment_proposal_command, @@ -844,13 +841,6 @@ def _main(argv: list[str] | None = None) -> int: if authority_shadow_result is not None: return authority_shadow_result - goal_acceptance_result = handle_goal_acceptance_command( - args, registry_path=registry_path, runtime_root_arg=args.runtime_root, - output_format=output_format, print_payload=print_payload, - ) - if goal_acceptance_result is not None: - return goal_acceptance_result - shared_goal_alignment_result = handle_shared_goal_alignment_command( args, registry_path=registry_path, diff --git a/loopx/cli_runtime.py b/loopx/cli_runtime.py index 3f243524c3..c62569e188 100644 --- a/loopx/cli_runtime.py +++ b/loopx/cli_runtime.py @@ -78,6 +78,7 @@ "handoff", "handoff-mode", "performance-diagnosis", "heartbeat-prompt", + "goal-acceptance", } @@ -399,6 +400,10 @@ def _build_selected_parser(command: str) -> LoopXArgumentParser: from .cli_commands.support_control_heartbeat_registration import register_heartbeat_control_commands register_heartbeat_control_commands(subparsers, add_subcommand_format) + elif command == "goal-acceptance": + from .cli_commands.goal_acceptance import register_goal_acceptance_command + + register_goal_acceptance_command(subparsers, add_subcommand_format) else: # pragma: no cover - caller guards the private interface raise ValueError(f"unsupported selected command: {command}") return parser @@ -418,6 +423,13 @@ def _dispatch_common_command( registry_was_supplied=not allow_missing_registry, print_payload=print_payload, output_format=output_format, ) + if args.command == "goal-acceptance": + from .cli_commands.goal_acceptance import handle_goal_acceptance_command + + return handle_goal_acceptance_command( + args, registry_path=registry_path, runtime_root_arg=args.runtime_root, + output_format=output_format, print_payload=print_payload, + ) if args.command == "performance-diagnosis": from .capabilities.performance_diagnosis.cli import handle_performance_diagnosis_command diff --git a/tests/test_cli_entrypoint.py b/tests/test_cli_entrypoint.py index a011c354a8..77a10a98cc 100644 --- a/tests/test_cli_entrypoint.py +++ b/tests/test_cli_entrypoint.py @@ -284,6 +284,7 @@ def test_command_reference_does_not_require_default_runtime_route( (["agent-context", "--help"], "loopx.cli_commands.agent_context", "loopx.cli_commands.agent_context"), (["agent-directory", "--help"], "loopx.cli_commands.agent_directory", "loopx.cli_commands.agent_directory"), (["agent-capabilities", "--help"], "loopx.cli_commands.agent_capabilities", "loopx.cli_commands.agent_capabilities"), + (["goal-acceptance", "--help"], "loopx.cli_commands.goal_acceptance", "loopx.cli_commands.goal_acceptance"), (["goal-portfolio", "--help"], "loopx.cli_commands.summary_all_registration", "loopx.cli_commands.summary_all"), (["global-summary", "--help"], "loopx.cli_commands.summary_all_registration", "loopx.cli_commands.summary_all"), (["global-gates", "--help"], "loopx.cli_commands.summary_all_registration", "loopx.cli_commands.summary_all"), @@ -482,6 +483,9 @@ def test_selected_parser_matches_full_help_and_diagnostics() -> None: argv_cases.extend([[command, "--help"], [command], [command, "--unknown-option"], [command, "--goal-id", "goal", "--agent-i", "agent"]]) argv_cases.append(["agent-context", "--goal-id", "goal", "--agent-id", "agent", "--phase", "unknown"]) + argv_cases.extend([["goal-acceptance", "--help"], ["goal-acceptance", "inspect"], + ["goal-acceptance", "unknown-action", "--goal-id", "goal"], + ["goal-acceptance", "inspect", "--goal-id", "goal", "--unknown-option"]]) selected = run_cli_batch("loopx.entrypoint", argv_cases) full = run_cli_batch("loopx.cli", argv_cases) @@ -621,6 +625,25 @@ def test_selected_lifecycle_refusals_match_without_full_cli(tmp_path, arguments) assert registry.read_bytes() == before +@pytest.mark.parametrize("arguments", [ + ["inspect"], + ["inspect-proposal", "--proposal-id", "missing-proposal"], + ["verify", "--proposal-id", "missing-proposal"], +]) +def test_selected_goal_acceptance_refusals_match_without_full_cli(tmp_path, arguments): + registry, runtime = write_command_fixture(tmp_path) + before = registry.read_bytes() + argv = ["--registry", str(registry), "--runtime-root", str(runtime), "--format", "json", + "goal-acceptance", *arguments, "--goal-id", "missing-goal"] + selected = run_cli_main("loopx.entrypoint", argv, forbidden_modules=("loopx.cli",)) + full = run_cli_main("loopx.cli", argv) + assert selected.returncode == full.returncode == 1 + assert selected.stdout == full.stdout + assert selected.stderr == full.stderr + assert json.loads(selected.stdout)["ok"] is False + assert registry.read_bytes() == before + + @pytest.mark.parametrize("provider", ["file", "sqlite"]) def test_selected_refresh_preview_keeps_canonical_state_without_full_cli(tmp_path, monkeypatch, provider): from tests.control_plane.canonical_authority_fixture import initialize_canonical_source_fixture, isolate_sqlite_runtime