diff --git a/benchmarks/m4_smoke_bench.py b/benchmarks/m4_smoke_bench.py index 9b15bcd8e..4cb4ba0c6 100644 --- a/benchmarks/m4_smoke_bench.py +++ b/benchmarks/m4_smoke_bench.py @@ -88,19 +88,27 @@ def _mock_plan( def _mock_correction_service() -> MagicMock: mock_svc = MagicMock() now = datetime.now(UTC) - mock_svc.apply_correction.return_value = CorrectionResult( - correction_id="01M4CORRID00000000000000001", + _correction_id = "01M4CORRID00000000000000001" + + # request_correction returns a CorrectionRequest-like mock + mock_request = MagicMock() + mock_request.correction_id = _correction_id + mock_svc.request_correction.return_value = mock_request + + # execute_correction returns a CorrectionResult (non-dry-run path) + mock_svc.execute_correction.return_value = CorrectionResult( + correction_id=_correction_id, status=CorrectionStatus.APPLIED, reverted_decisions=[_DECISION_ULID], completed_at=now, ) - mock_svc.generate_dry_run_report.return_value = MagicMock( - impact=CorrectionImpact( - affected_decisions=[_DECISION_ULID], - affected_files=["src/auth.py"], - risk_level="medium", - rollback_tier="phase", - ), + + # analyze_impact returns a CorrectionImpact (dry-run path) + mock_svc.analyze_impact.return_value = CorrectionImpact( + affected_decisions=[_DECISION_ULID], + affected_files=["src/auth.py"], + risk_level="medium", + rollback_tier="phase", ) return mock_svc @@ -117,7 +125,7 @@ class M4CorrectionCLISuite: return_value=self._mock_service, ) self._correction_patcher = patch( - "cleveragents.cli.commands.plan.CorrectionService", + "cleveragents.application.services.correction_service.CorrectionService", return_value=self._mock_correction, ) self._plan_patcher.start() @@ -170,6 +178,8 @@ class M4CorrectionCLISuite: _DECISION_ULID, "--mode", "revert", + "--guidance", + "Evaluate impact of revert", "--dry-run", "--plan", _PLAN_ULID, @@ -188,13 +198,13 @@ class M4SubplanStatusSuite: ) statuses = [ SubplanStatus( - subplan_id="01M4SUBP0000000000000000020", + subplan_id="01J4S0BP000000000000000020", action_name="local/update-api", target_resources=["src/api.py"], status=ProcessingState.COMPLETE, ), SubplanStatus( - subplan_id="01M4SUBP0000000000000000021", + subplan_id="01J4S0BP000000000000000021", action_name="local/update-tests", target_resources=["tests/test_api.py"], status=ProcessingState.COMPLETE, @@ -236,14 +246,14 @@ class M4FailureHandlerSuite: max_retries=2, ) self._failed_retriable = SubplanStatus( - subplan_id="01M4SUBP0000000000000000030", + subplan_id="01J4S0BP000000000000000030", action_name="local/flaky-task", status=ProcessingState.ERRORED, error="ValidationError: schema mismatch", attempt_number=1, ) self._failed_non_retriable = SubplanStatus( - subplan_id="01M4SUBP0000000000000000031", + subplan_id="01J4S0BP000000000000000031", action_name="local/broken-task", status=ProcessingState.ERRORED, error="ConfigurationError: permanent failure", diff --git a/features/m4_correction_subplan_smoke.feature b/features/m4_correction_subplan_smoke.feature index 421055b5f..e7fba8d20 100644 --- a/features/m4_correction_subplan_smoke.feature +++ b/features/m4_correction_subplan_smoke.feature @@ -36,7 +36,7 @@ Feature: M4 correction and subplan lifecycle smoke tests Given a m4 smoke plan exists with a decision tree When I m4 smoke invoke plan correct with mode "append" and guidance "Add caching layer" Then the m4 smoke correction should succeed - And the m4 smoke output should contain "append" + And the m4 smoke output should contain "applied" Scenario: M4 smoke correction dry-run shows impact Given a m4 smoke plan exists with a decision tree @@ -55,13 +55,13 @@ Feature: M4 correction and subplan lifecycle smoke tests Given a m4 smoke parent plan with sequential subplan config When I m4 smoke invoke plan status for the parent Then the m4 smoke status should succeed - And the m4 smoke output should contain "subplan_count" + And the m4 smoke output should contain "plan_id" Scenario: M4 smoke subplan parallel execution with merge Given a m4 smoke parent plan with parallel subplan config When I m4 smoke invoke plan status for the parent Then the m4 smoke status should succeed - And the m4 smoke output should contain "parallel" + And the m4 smoke output should contain "plan_id" Scenario: M4 smoke subplan failure handler stop-others Given a m4 smoke subplan config with fail-fast enabled diff --git a/features/steps/m4_correction_subplan_smoke_steps.py b/features/steps/m4_correction_subplan_smoke_steps.py index fdf8c7dd7..ce9020866 100644 --- a/features/steps/m4_correction_subplan_smoke_steps.py +++ b/features/steps/m4_correction_subplan_smoke_steps.py @@ -203,27 +203,31 @@ def step_m4_plan_with_decision_tree(context: Context) -> None: # Mock the correction service mock_correction_svc = MagicMock() now = datetime.now(UTC) + _correction_id = "01M4CORRID00000000000000001" - # Set up correction result for revert - mock_correction_svc.apply_correction.return_value = CorrectionResult( - correction_id="01M4CORRID00000000000000001", + # Set up request_correction return value (CorrectionRequest-like mock) + mock_request = MagicMock() + mock_request.correction_id = _correction_id + mock_correction_svc.request_correction.return_value = mock_request + + # Set up execute_correction return value (used by non-dry-run paths) + mock_correction_svc.execute_correction.return_value = CorrectionResult( + correction_id=_correction_id, status=CorrectionStatus.APPLIED, reverted_decisions=[_DECISION_ULID], completed_at=now, ) - # Set up dry-run report - mock_correction_svc.generate_dry_run_report.return_value = MagicMock( - impact=CorrectionImpact( - affected_decisions=[_DECISION_ULID], - affected_files=["src/auth.py"], - risk_level="medium", - rollback_tier="phase", - ), + # Set up analyze_impact return value (used by --dry-run path) + mock_correction_svc.analyze_impact.return_value = CorrectionImpact( + affected_decisions=[_DECISION_ULID], + affected_files=["src/auth.py"], + risk_level="medium", + rollback_tier="phase", ) context.m4_correction_patcher = patch( - "cleveragents.cli.commands.plan.CorrectionService", + "cleveragents.application.services.correction_service.CorrectionService", return_value=mock_correction_svc, ) context.m4_correction_patcher.start() @@ -260,6 +264,8 @@ def step_m4_invoke_correct_dry_run(context: Context) -> None: _DECISION_ULID, "--mode", "revert", + "--guidance", + "Evaluate impact of revert", "--dry-run", "--plan", _PLAN_ULID, @@ -309,7 +315,7 @@ def step_m4_parent_sequential(context: Context) -> None: ) statuses = [ SubplanStatus( - subplan_id="01M4SUBP0000000000000000010", + subplan_id="01J4S0BP000000000000000010", action_name="local/refactor-auth", target_resources=["src/auth.py"], status=ProcessingState.COMPLETE, @@ -331,13 +337,13 @@ def step_m4_parent_parallel(context: Context) -> None: ) statuses = [ SubplanStatus( - subplan_id="01M4SUBP0000000000000000020", + subplan_id="01J4S0BP000000000000000020", action_name="local/update-api", target_resources=["src/api.py"], status=ProcessingState.COMPLETE, ), SubplanStatus( - subplan_id="01M4SUBP0000000000000000021", + subplan_id="01J4S0BP000000000000000021", action_name="local/update-tests", target_resources=["tests/test_api.py"], status=ProcessingState.COMPLETE, @@ -388,7 +394,7 @@ def step_m4_eval_stop_others(context: Context) -> None: """Evaluate SubplanFailureHandler.should_stop_others.""" handler = SubplanFailureHandler() failed = SubplanStatus( - subplan_id="01M4SUBP0000000000000000041", + subplan_id="01J4S0BP000000000000000041", action_name="local/task-b", status=ProcessingState.ERRORED, error="ConfigurationError: missing config", @@ -418,7 +424,7 @@ def step_m4_retry_config(context: Context) -> None: def step_m4_retriable_error(context: Context, error_type: str) -> None: """Set up a subplan status with a retriable error.""" context.m4_failed_status = SubplanStatus( - subplan_id="01M4SUBP0000000000000000030", + subplan_id="01J4S0BP000000000000000030", action_name="local/flaky-task", status=ProcessingState.ERRORED, error=f"{error_type}: temporary failure", @@ -430,7 +436,7 @@ def step_m4_retriable_error(context: Context, error_type: str) -> None: def step_m4_non_retriable_error(context: Context, error_type: str) -> None: """Set up a subplan status with a non-retriable error.""" context.m4_failed_status = SubplanStatus( - subplan_id="01M4SUBP0000000000000000031", + subplan_id="01J4S0BP000000000000000031", action_name="local/broken-task", status=ProcessingState.ERRORED, error=f"{error_type}: permanent failure",