@extensibility @custom_sandbox_strategy
Feature: Custom Sandbox Strategy Registration via SandboxStrategy Protocol
  As a CleverAgents developer
  I want to register custom sandbox strategies via configuration
  So that specialized resource types can use domain-specific isolation

  # ---------------------------------------------------------------------------
  # SandboxRef model
  # ---------------------------------------------------------------------------

  @sandbox_ref
  Scenario: SandboxRef is a frozen dataclass
    Given a SandboxRef with sandbox_id "sb-001" and plan_id "plan-001"
    Then the SandboxRef sandbox_id should be "sb-001"
    And the SandboxRef plan_id should be "plan-001"
    And the SandboxRef should be immutable

  @sandbox_ref
  Scenario: SandboxRef carries metadata
    Given a SandboxRef with metadata key "backend" value "redis"
    Then the SandboxRef metadata should contain key "backend"

  # ---------------------------------------------------------------------------
  # DiffView / DiffEntry models
  # ---------------------------------------------------------------------------

  @diff_view
  Scenario: DiffEntry creation with required fields
    Given a DiffEntry with path "src/main.py" and operation "modified"
    Then the DiffEntry path should be "src/main.py"
    And the DiffEntry operation should be "modified"

  @diff_view
  Scenario: DiffView aggregates entries
    Given a DiffView with 3 entries
    Then the DiffView should have 3 entries
    And the DiffView should have a sandbox_id

  @diff_view
  Scenario: DiffEntry rejects empty path
    When I attempt to create a DiffEntry with empty path
    Then a sandbox strategy validation error should be raised

  # ---------------------------------------------------------------------------
  # SandboxStrategyProtocol
  # ---------------------------------------------------------------------------

  @sandbox_strategy_protocol
  Scenario: SandboxStrategyProtocol is runtime_checkable
    Given the SandboxStrategyProtocol is available
    Then it should be a runtime_checkable Protocol

  @sandbox_strategy_protocol
  Scenario: A class implementing all 9 methods satisfies the Protocol
    Given a mock class implementing all 9 SandboxStrategy methods
    Then the mock class should satisfy SandboxStrategyProtocol

  @sandbox_strategy_protocol
  Scenario: A class missing methods does not satisfy the Protocol
    Given a class missing the checkpoint method
    Then the class should not satisfy SandboxStrategyProtocol

  # ---------------------------------------------------------------------------
  # SandboxStrategyRegistry
  # ---------------------------------------------------------------------------

  @strategy_registry
  Scenario: Register a valid custom strategy
    Given a SandboxStrategyRegistry with test allowed prefixes
    And a valid custom strategy class in "cleveragents.infrastructure.sandbox.strategy_adapter"
    When I register the strategy as "test_adapter"
    Then the registry should contain strategy "test_adapter"
    And listing strategies should include "test_adapter"

  @strategy_registry
  Scenario: Registering a non-protocol class raises ProtocolMismatchError
    Given a SandboxStrategyRegistry with test allowed prefixes
    When I attempt to register a non-protocol class as "bad_strategy"
    Then a sandbox ProtocolMismatchError should be raised

  @strategy_registry
  Scenario: Register with empty name raises ValueError
    Given a SandboxStrategyRegistry with test allowed prefixes
    When I attempt to register a strategy with empty name
    Then a sandbox ValueError should be raised

  @strategy_registry
  Scenario: Get unregistered strategy returns None
    Given an empty SandboxStrategyRegistry
    When I look up strategy "nonexistent"
    Then the strategy lookup result should be None

  @strategy_registry
  Scenario: Register all from config dictionary
    Given a SandboxStrategyRegistry with test allowed prefixes
    And a config dictionary with 2 custom strategies
    When I register all from config
    Then 2 strategies should be registered

  @strategy_registry
  Scenario: Clear removes all strategies
    Given a SandboxStrategyRegistry with one registered strategy
    When I clear the sandbox strategy registry
    Then the sandbox strategy registry should be empty

  # ---------------------------------------------------------------------------
  # BuiltInSandboxStrategyAdapter
  # ---------------------------------------------------------------------------

  @adapter
  Scenario: Adapter create returns SandboxRef
    Given a BuiltInSandboxStrategyAdapter for "none" strategy
    And a test Resource with location
    When I call adapter.create with plan "plan-001"
    Then I should get a SandboxRef with plan_id "plan-001"

  @adapter
  Scenario: Adapter write creates a DiffEntry
    Given a BuiltInSandboxStrategyAdapter for "copy_on_write" strategy
    And a test Resource with a temporary directory
    And the adapter sandbox is created for plan "plan-write"
    When I call adapter.write with path "test.txt" and content "hello"
    Then I should get a DiffEntry with operation "added"

  @adapter
  Scenario: Adapter read returns file content
    Given a BuiltInSandboxStrategyAdapter for "copy_on_write" strategy
    And a test Resource with a temporary directory
    And the adapter sandbox is created for plan "plan-read"
    And I write "data" to "read_test.txt" via the adapter
    When I call adapter.read for "read_test.txt"
    Then I should get content "data"

  @adapter
  Scenario: Adapter diff returns DiffView
    Given a BuiltInSandboxStrategyAdapter for "none" strategy
    And a test Resource with location
    And the adapter sandbox is created for plan "plan-diff"
    When I call adapter.diff
    Then I should get a DiffView

  @adapter
  Scenario: Adapter checkpoint and restore
    Given a BuiltInSandboxStrategyAdapter for "copy_on_write" strategy
    And a test Resource with a temporary directory
    And the adapter sandbox is created for plan "plan-cp"
    And I write "v1" to "cp_test.txt" via the adapter
    And I checkpoint as "cp-1"
    And I write "v2" to "cp_test.txt" via the adapter
    When I restore checkpoint "cp-1"
    Then reading "cp_test.txt" should return "v1"

  @adapter
  Scenario: Adapter cleanup releases resources
    Given a BuiltInSandboxStrategyAdapter for "none" strategy
    And a test Resource with location
    And the adapter sandbox is created for plan "plan-cleanup"
    When I call adapter.cleanup
    Then the adapter should have no tracked sandboxes

  # ---------------------------------------------------------------------------
  # CustomStrategyConfig
  # ---------------------------------------------------------------------------

  @config
  Scenario: CustomStrategyConfig rejects empty name
    When I attempt to create a CustomStrategyConfig with empty name
    Then a sandbox ValueError should be raised

  @config
  Scenario: CustomStrategyConfig rejects empty module
    When I attempt to create a CustomStrategyConfig with empty module
    Then a sandbox ValueError should be raised

  @config
  Scenario: CustomStrategyConfig rejects empty class_name
    When I attempt to create a CustomStrategyConfig with empty class_name
    Then a sandbox ValueError should be raised

  # ---------------------------------------------------------------------------
  # Factory custom strategy integration
  # ---------------------------------------------------------------------------

  @factory_integration
  Scenario: Factory reports custom strategy availability
    Given a SandboxFactory with a custom registry containing "my_custom"
    Then the factory should report "my_custom" as a custom strategy
    And the factory should not report "nonexistent" as a custom strategy

  @factory_integration
  Scenario: Factory without registry reports no custom strategies
    Given a SandboxFactory without a custom registry
    Then the factory should not report "anything" as a custom strategy
