Files
cleveragents-core/features/error_recovery.feature
2026-02-25 10:07:09 +00:00

332 lines
16 KiB
Gherkin

Feature: Error recovery patterns and CLI hints
As a plan executor
I want structured error recovery with retry logic and actionable hints
So that failed plans can be recovered automatically or with clear guidance
Background:
Given an error recovery test environment
# -- Error classification ------------------------------------------------
Scenario: Classify transient error by message
When I classify the error message "Connection timed out after 30s"
Then the classified error category should be "transient"
Scenario: Classify validation error by message
When I classify the error message "Schema validation failed for field X"
Then the classified error category should be "validation"
Scenario: Classify configuration error by message
When I classify the error message "Missing configuration for provider"
Then the classified error category should be "configuration"
Scenario: Classify merge conflict error by message
When I classify the error message "Merge conflict in src/main.py"
Then the classified error category should be "merge_conflict"
Scenario: Classify authentication error by message
When I classify the error message "Authentication failed: invalid token"
Then the classified error category should be "authentication"
Scenario: Classify provider error by message
When I classify the error message "Model not available: gpt-4-turbo"
Then the classified error category should be "provider"
Scenario: Classify internal error by message
When I classify the error message "Unexpected internal error in pipeline"
Then the classified error category should be "internal"
Scenario: Classify unknown error by message
When I classify the error message "Something completely novel happened"
Then the classified error category should be "unknown"
Scenario: Classify error by exception type
When I classify error text "failed" using exception type "RateLimitError"
Then the classified error category should be "transient"
Scenario: Classify error by exception type overrides message
When I classify error text "validation issue" using exception type "NetworkError"
Then the classified error category should be "transient"
# -- Recovery hints ------------------------------------------------------
Scenario: Transient error recovery hints include retry command
When I get recovery hints for category "transient" and plan "01PLAN001"
Then the first recovery hint action should be "retry"
And the first recovery hint should have CLI command containing "01PLAN001"
Scenario: Validation error recovery hints include correct command
When I get recovery hints for category "validation" and plan "01PLAN001"
Then the first recovery hint action should be "correct"
And the first recovery hint should have CLI command containing "correct"
Scenario: Configuration error recovery hints suggest cancel
When I get recovery hints for category "configuration" and plan "01PLAN001"
Then the first recovery hint action should be "cancel"
Scenario: Merge conflict hints suggest revert to strategize
When I get recovery hints for category "merge_conflict" and plan "01PLAN001"
Then the first recovery hint action should be "revert_strategize"
# -- Error recording -----------------------------------------------------
Scenario: Record error creates error record with metadata
Given a plan in errored state for error recovery
When I record an error with phase "execute" and message "Timeout talking to provider"
Then the recorded error should have category "transient"
And the recorded error should have phase "execute"
And the recorded error should have recovery hints
And the error history should have 1 record
Scenario: Record error with explicit category
Given a plan in errored state for error recovery
When I record a categorized error with phase "apply" and message "custom issue" and category "merge_conflict"
Then the recorded error should have category "merge_conflict"
Scenario: Record multiple errors tracks retry count
Given a plan in errored state for error recovery
When I record an error with phase "execute" and message "Timeout 1"
And I record an error with phase "execute" and message "Timeout 2"
Then the error history should have 2 records
And the latest error retry count should be 1
# -- Retry policy --------------------------------------------------------
Scenario: Transient error with auto-retry threshold 0 is retriable
Given a plan in errored state for error recovery
And the auto retry threshold is 0.0
When I record an error with phase "execute" and message "Connection timed out"
Then the service should recommend retry
Scenario: Transient error with auto-retry threshold 1 requires escalation
Given a plan in errored state for error recovery
And the auto retry threshold is 1.0
When I record an error with phase "execute" and message "Connection timed out"
Then the service should recommend escalation
Scenario: Configuration error is never retriable
Given a plan in errored state for error recovery
And the auto retry threshold is 0.0
When I record an error with phase "strategize" and message "Missing configuration"
Then the service should recommend escalation
Scenario: Retry exhaustion after max retries
Given a plan in errored state for error recovery
And the max retries is 2
When I record an error with phase "execute" and message "Timeout 1"
And I record an error with phase "execute" and message "Timeout 2"
And I record an error with phase "execute" and message "Timeout 3"
Then the latest error should have retries exhausted
And the service should recommend escalation
# -- Error history -------------------------------------------------------
Scenario: Error history shows category breakdown
Given a plan in errored state for error recovery
When I record an error with phase "execute" and message "Timeout occurred"
And I record an error with phase "apply" and message "Merge conflict in file.py"
Then the error history category breakdown should include "transient"
And the error history category breakdown should include "merge_conflict"
# -- CLI output formatting -----------------------------------------------
Scenario: Format error output as plain text
Given a plan in errored state for error recovery
When I record an error with phase "execute" and message "Provider timed out"
And I format the error output as "plain"
Then the error recovery output should contain "transient"
And the error recovery output should contain "Provider timed out"
And the error recovery output should contain "execute"
Scenario: Format error output as JSON
Given a plan in errored state for error recovery
When I record an error with phase "execute" and message "Provider timed out"
And I format the error output as "json"
Then the error recovery output should contain "transient"
And the error recovery output should contain "plan_id"
Scenario: Format empty error history
When I format error output as "plain" for separate plan "01EMPTYPLAN00000000001"
Then the error recovery output should contain "No errors recorded"
# -- ErrorRecord model checks --------------------------------------------
Scenario: ErrorRecord is_retriable for transient errors
Given an error record with category "transient" and retry count 0 and max retries 3
Then the error record should be retriable
Scenario: ErrorRecord is not retriable when exhausted
Given an error record with category "transient" and retry count 3 and max retries 3
Then the error record should not be retriable
And the error record should have retries exhausted
Scenario: ErrorRecord is not retriable for configuration errors
Given an error record with category "configuration" and retry count 0 and max retries 3
Then the error record should not be retriable
Scenario: ErrorRecord to_cli_dict has expected fields
Given an error recovery test environment
Given an error record with category "transient" and retry count 1 and max retries 3
Then the error record CLI dict should have key "error_id"
And the error record CLI dict should have key "category"
And the error record CLI dict should have key "is_retriable"
# -- Exception-based recording -------------------------------------------
Scenario: Record error with actual exception captures stack trace
Given a plan in errored state for error recovery
When I record an error with exception for phase "execute" and message "timeout"
Then the recorded error should have a stack summary
# -- Edge cases -----------------------------------------------------------
Scenario: should_retry returns false when no errors exist
Given a plan in errored state for error recovery
Then the service should not recommend retry for a clean plan
Scenario: should_escalate returns false when no errors exist
Given a plan in errored state for error recovery
Then the service should not recommend escalation for a clean plan
Scenario: Format recovery hints for CLI
Given a plan in errored state for error recovery
When I record an error with phase "execute" and message "Connection timed out"
Then the CLI recovery hints should contain "transient"
# -- ErrorRecoveryPolicy formatting --------------------------------------
Scenario: Policy formats output with actor and tool info
Given a plan in errored state for error recovery
When I record an actor-error in phase "execute" with message "Timeout" actor "test-actor" tool "test-tool"
And I format the error output as "plain"
Then the error recovery output should contain "test-actor"
And the error recovery output should contain "test-tool"
Scenario: Policy format shows automatic retry available
Given a plan in errored state for error recovery
And the auto retry threshold is 0.0
When I record an error with phase "execute" and message "Connection timed out"
And I format the error output as "plain"
Then the error recovery output should contain "Automatic retry available"
Scenario: Policy format shows human intervention required
Given a plan in errored state for error recovery
And the auto retry threshold is 1.0
When I record an error with phase "execute" and message "Connection timed out"
And I format the error output as "plain"
Then the error recovery output should contain "Human intervention required"
Scenario: Policy format shows retries exhausted message
Given a plan in errored state for error recovery
And the max retries is 1
When I record an error with phase "execute" and message "Timeout 1"
And I record an error with phase "execute" and message "Timeout 2"
And I format the error output as "plain"
Then the error recovery output should contain "retry attempts exhausted"
# -- ErrorHistory with no retriable errors --------------------------------
Scenario: Error history has_retriable is false when only non-retriable
Given a plan in errored state for error recovery
When I record an error with phase "strategize" and message "Missing configuration"
Then the error history should not have retriable errors
# -- ErrorRecord with actor and tool for CLI dict -------------------------
Scenario: ErrorRecord to_cli_dict includes actor and tool when present
Given an error record with actor "my-actor" and tool "my-tool"
Then the error record CLI dict should have key "actor"
And the error record CLI dict should have key "tool_call"
# -- Recovery hints for empty plan ----------------------------------------
Scenario: Format recovery hints returns empty for plan with no errors
Given a plan in errored state for error recovery
Then the CLI recovery hints for a clean plan should be empty
# -- ErrorRecord to_cli_dict with stack summary ---------------------------
Scenario: ErrorRecord to_cli_dict includes stack summary
Given an error record with stack summary "File test.py line 10"
Then the error record CLI dict should have key "stack_summary"
# -- ErrorRecoveryPolicy should_retry on non-retriable --------------------
Scenario: Policy should_retry returns false for non-retriable category
Given a policy with threshold 0.0 and max retries 3
And an error record with category "configuration" and retry count 0 and max retries 3
Then the policy should not recommend retry
Scenario: Policy should_retry returns false for exhausted retries
Given a policy with threshold 0.0 and max retries 3
And an error record with category "transient" and retry count 3 and max retries 3
Then the policy should not recommend retry
# -- T1: Persistence failure path ----------------------------------------
Scenario: Persist error metadata handles lifecycle errors gracefully
Given a plan in errored state for error recovery
And the lifecycle service update_error_details raises an error
When I record an error with phase "execute" and message "transient failure"
Then the error should still be recorded in memory
# -- T2: Branch coverage for policy/format --------------------------------
Scenario: Policy should_escalate returns true for exhausted retries
Given a policy with threshold 0.0 and max retries 3
And an error record with category "transient" and retry count 3 and max retries 3
Then the policy should recommend escalation
Scenario: Policy should_escalate returns true for non-retriable category
Given a policy with threshold 0.0 and max retries 3
And an error record with category "configuration" and retry count 0 and max retries 3
Then the policy should recommend escalation
Scenario: Format recovery output includes hints with CLI commands
Given a policy with threshold 0.0 and max retries 3
And a hinted error record with category "transient" and retry count 0 and max retries 3
Then the formatted recovery output should contain CLI command text
Scenario: Format recovery output shows exhausted message
Given a policy with threshold 0.0 and max retries 3
And a hinted error record with category "transient" and retry count 3 and max retries 3
Then the formatted recovery output should contain "exhausted"
Scenario: Format recovery output shows automatic retry available
Given a policy with threshold 0.0 and max retries 3
And an error record with category "transient" and retry count 0 and max retries 3
Then the formatted recovery output should contain "Automatic retry"
Scenario: Policy should_retry returns false when threshold is 1.0
Given a policy with threshold 1.0 and max retries 3
And an error record with category "transient" and retry count 0 and max retries 3
Then the policy should not recommend retry
# -- T4: Integration test with executor retry loop ------------------------
Scenario: Executor retries execute phase on recoverable errors
Given a plan executor with error recovery service
And the execute actor fails twice then succeeds
When I run the executor for the plan
Then the execute should complete after retries
And the error recovery service should have recorded 2 errors
Scenario: Executor fails after exhausting retries
Given a plan executor with error recovery service max retries 2
And the execute actor always fails with "persistent failure"
When I run the executor expecting failure
Then the lifecycle should record the plan as failed
And the error recovery service should have recorded 3 errors
# -- B5: Bare conflict no longer matches MERGE_CONFLICT -------------------
Scenario: Classify resource conflict does not match merge_conflict
When I classify the error message "resource conflict detected"
Then the classified error category should not be "merge_conflict"
# -- S1: Format string safe substitution ----------------------------------
Scenario: Recovery hints handle plan_id with braces safely
When I get recovery hints for category "transient" with plan_id "{evil_id}"
Then the hints should contain "{evil_id}" in CLI commands