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
