Feature: Consolidated Misc
  Combined scenarios from: a2a_facade, agents_base_uncovered_lines, bridge_coverage, coverage_improvements, m6_autonomy_acceptance, scale_test, container_coverage_r2, lsp_registry, memory_service_coverage, migration_runner_coverage, system_coverage_boost, yaml_engine_direct_coverage

  # ============================================================
  # Originally from: a2a_facade.feature
  # Feature: A2A Local Facade and Server Stubs
  # ============================================================

  Scenario: Create facade with no services
    Given a new A2aLocalFacade with no services
    Then the facade should be created successfully


  Scenario: Create facade with services dict
    Given a new A2aLocalFacade with services {"session": "mock_session"}
    Then the facade should be created successfully


  Scenario: Register a service on the facade
    Given a new A2aLocalFacade with no services
    When I register a service named "planner" on the facade
    Then the service should be registered successfully


  Scenario: Register service with empty name raises error
    Given a new A2aLocalFacade with no services
    When I try to register a service with an empty name
    Then an A2A ValueError should be raised

    # -----------------------------------------------------------------------
    # A2aLocalFacade — dispatch for each supported operation
    # -----------------------------------------------------------------------


  Scenario: Dispatch session.create returns session_id and status
    Given a new A2aLocalFacade with no services
    When I dispatch operation "session.create" with params {}
    Then the response status should be "ok"
    And response data includes key "session_id"
    And response data key "status" equals "created"


  Scenario: Dispatch session.close returns status closed
    Given a new A2aLocalFacade with no services
    When I dispatch operation "session.close" with params {}
    Then the response status should be "ok"
    And response data key "status" equals "closed"


  Scenario: Dispatch plan.create returns plan_id and status
    Given a new A2aLocalFacade with no services
    When I dispatch operation "plan.create" with params {}
    Then the response status should be "ok"
    And response data includes key "plan_id"
    And response data key "status" equals "created"


  Scenario: Dispatch plan.execute returns plan_id and status queued
    Given a new A2aLocalFacade with no services
    When I dispatch operation "plan.execute" with params {"plan_id": "PLAN001"}
    Then the response status should be "ok"
    And response data key "plan_id" equals "PLAN001"
    And response data key "status" equals "queued"


  Scenario: Dispatch plan.status returns plan_id and phase
    Given a new A2aLocalFacade with no services
    When I dispatch operation "plan.status" with params {"plan_id": "PLAN001"}
    Then the response status should be "ok"
    And response data key "plan_id" equals "PLAN001"
    And response data key "phase" equals "unknown"


  Scenario: Dispatch plan.diff returns plan_id and empty changes
    Given a new A2aLocalFacade with no services
    When I dispatch operation "plan.diff" with params {"plan_id": "PLAN001"}
    Then the response status should be "ok"
    And response data key "plan_id" equals "PLAN001"
    And response data includes key "changes"


  Scenario: Dispatch plan.apply returns plan_id and status applied
    Given a new A2aLocalFacade with no services
    When I dispatch operation "plan.apply" with params {"plan_id": "PLAN001"}
    Then the response status should be "ok"
    And response data key "plan_id" equals "PLAN001"
    And response data key "status" equals "applied"


  Scenario: Dispatch registry.list_tools returns empty tools
    Given a new A2aLocalFacade with no services
    When I dispatch operation "registry.list_tools" with params {}
    Then the response status should be "ok"
    And response data includes key "tools"


  Scenario: Dispatch registry.list_resources returns empty resources
    Given a new A2aLocalFacade with no services
    When I dispatch operation "registry.list_resources" with params {}
    Then the response status should be "ok"
    And response data includes key "resources"


  Scenario: Dispatch context.get returns empty context
    Given a new A2aLocalFacade with no services
    When I dispatch operation "context.get" with params {}
    Then the response status should be "ok"
    And response data includes key "context"


  Scenario: Dispatch event.subscribe returns subscription_id and status
    Given a new A2aLocalFacade with no services
    When I dispatch operation "event.subscribe" with params {}
    Then the response status should be "ok"
    And response data includes key "subscription_id"
    And response data key "status" equals "subscribed"

    # -----------------------------------------------------------------------
    # A2aLocalFacade — unknown operation
    # -----------------------------------------------------------------------


  Scenario: Dispatch unknown operation raises A2aOperationNotFoundError
    Given a new A2aLocalFacade with no services
    When I dispatch an unknown operation "does.not.exist"
    Then an A2aOperationNotFoundError should be raised
    And the error operation attribute should be "does.not.exist"

    # -----------------------------------------------------------------------
    # A2aLocalFacade — list_operations
    # -----------------------------------------------------------------------


  Scenario: list_operations returns all supported operations
    Given a new A2aLocalFacade with no services
    When I call list_operations on the facade
    Then the operations list should contain "session.create"
    And the operations list should contain "plan.create"
    And the operations list should contain "plan.execute"
    And the operations list should contain "registry.list_tools"
    And the operations list should contain "context.get"
    And the operations list should contain "event.subscribe"
    And the operations list should have 42 items

    # -----------------------------------------------------------------------
    # A2aHttpTransport — all stubs raise A2aNotAvailableError
    # -----------------------------------------------------------------------


  Scenario: Transport send raises A2aNotAvailableError
    Given a new A2aHttpTransport
    When I try to send a request via the transport
    Then an A2aNotAvailableError should be raised


  Scenario: Transport connect raises A2aNotAvailableError
    Given a new A2aHttpTransport
    When I try to connect via the transport to "http://localhost:8080"
    Then an A2aNotAvailableError should be raised


  Scenario: Transport disconnect raises A2aNotAvailableError
    Given a new A2aHttpTransport
    When I try to disconnect the transport
    Then an A2aNotAvailableError should be raised


  Scenario: Transport is_connected returns False
    Given a new A2aHttpTransport
    Then the transport should not be connected

    # -----------------------------------------------------------------------
    # A2aEventQueue — local mode works
    # -----------------------------------------------------------------------


  Scenario: Publish event to local queue
    Given a new A2aEventQueue
    When I publish an event with type "plan.started"
    Then the event queue should have 1 event


  Scenario: Subscribe locally and receive event
    Given a new A2aEventQueue
    And I subscribe locally with a callback
    When I publish an event with type "plan.completed"
    Then the callback should have been called with event type "plan.completed"


  Scenario: Unsubscribe removes subscription
    Given a new A2aEventQueue
    And I subscribe locally with a callback
    When I unsubscribe using the subscription id
    Then the unsubscribe should return True


  Scenario: Unsubscribe non-existent returns False
    Given a new A2aEventQueue
    When I unsubscribe using a non-existent subscription id
    Then the unsubscribe should return False


  Scenario: Get events respects limit
    Given a new A2aEventQueue
    When I publish 5 events
    And I get events with limit 3
    Then I should receive 3 events

    # -----------------------------------------------------------------------
    # A2aEventQueue — remote stub raises
    # -----------------------------------------------------------------------


  Scenario: Remote subscribe raises A2aNotAvailableError
    Given a new A2aEventQueue
    When I try to subscribe remotely to "http://remote:9090/events"
    Then an A2aNotAvailableError should be raised

    # -----------------------------------------------------------------------
    # A2aVersionNegotiator
    # -----------------------------------------------------------------------


  Scenario: Negotiate supported version succeeds
    Given a new A2aVersionNegotiator
    When I negotiate version "2.0"
    Then the negotiated version should be "2.0"


  Scenario: Negotiate unsupported version raises error
    Given a new A2aVersionNegotiator
    When I try to negotiate version "99.0"
    Then an A2aVersionMismatchError should be raised
    And the error requested_version should be "99.0"


  Scenario: is_supported returns True for valid version
    Given a new A2aVersionNegotiator
    Then version "2.0" should be supported


  Scenario: is_supported returns False for invalid version
    Given a new A2aVersionNegotiator
    Then version "99.0" should not be supported


  Scenario: get_current returns current version
    Given a new A2aVersionNegotiator
    Then the current version should be "2.0"

    # -----------------------------------------------------------------------
    # A2aRequest model validation
    # -----------------------------------------------------------------------


  Scenario: A2aRequest with valid operation succeeds
    When I create an A2aRequest with operation "session.create"
    Then the request should have a non-empty request_id
    And the request a2a_version should be "2.0"


  Scenario: A2aRequest with empty operation fails validation
    When I try to create an A2aRequest with empty operation
    Then a validation error should be raised

    # -----------------------------------------------------------------------
    # A2aResponse model validation
    # -----------------------------------------------------------------------


  Scenario: A2aResponse with valid status ok succeeds
    When I create an A2aResponse with status "ok" and request_id "REQ001"
    Then the response should be valid



  Scenario: A2aResponse with invalid status fails validation
    When I try to create an A2aResponse with status "maybe"
    Then a validation error should be raised

    # -----------------------------------------------------------------------
    # A2aErrorDetail model validation
    # -----------------------------------------------------------------------


  Scenario: A2aErrorDetail with valid fields succeeds
    When I create an A2aErrorDetail with code "NOT_FOUND" and message "gone"
    Then the error detail should be valid


  Scenario: A2aErrorDetail with empty code fails validation
    When I try to create an A2aErrorDetail with empty code
    Then a validation error should be raised

    # -----------------------------------------------------------------------
    # A2aEvent model construction
    # -----------------------------------------------------------------------


  Scenario: A2aEvent auto-generates event_id and timestamp
    When I create an A2aEvent with type "plan.progress"
    Then the event should have a non-empty event_id
    And the event should have a non-empty timestamp

    # -----------------------------------------------------------------------
    # Error hierarchy
    # -----------------------------------------------------------------------


  Scenario: A2aError is a CleverAgentsError
    Then A2aError should be a subclass of CleverAgentsError


  Scenario: A2aNotAvailableError is an A2aError
    Then A2aNotAvailableError should be a subclass of A2aError


  Scenario: A2aVersionMismatchError is an A2aError
    Then A2aVersionMismatchError should be a subclass of A2aError


  Scenario: A2aOperationNotFoundError is an A2aError
    Then A2aOperationNotFoundError should be a subclass of A2aError


  Scenario: A2aNotAvailableError has default message
    When I create an A2aNotAvailableError with default message
    Then the error message should contain "not available in local mode"


  # ============================================================
  # Originally from: agents_base_uncovered_lines.feature
  # Feature: Agents base uncovered lines
  # ============================================================

  Scenario: Processing pipeline schedules the process wrapper
    Given a basic test agent setup
    When I patch asyncio create_task and send a message
    Then the processing pipeline should schedule the process wrapper task


  Scenario: Process wrapper handles tuple input and emits result
    Given a basic test agent setup
    When I process a tuple message through the wrapper
    Then the tuple message and context should be passed and emitted


  Scenario: Process wrapper handles non-tuple input with empty context
    Given a basic test agent setup
    When I process a non tuple message through the wrapper
    Then the non tuple message should be processed with empty context


  Scenario: Process wrapper wraps exceptions with ExecutionError
    Given a basic test agent setup
    When process_message raises an error inside the wrapper
    Then an ExecutionError should be raised with the agent name


  Scenario: process_message_sync runs coroutine on the event loop
    Given a basic test agent setup
    When I call process_message_sync on the agent
    Then the synchronous result should match the async process output


  Scenario: Dispose cleans up streams that expose dispose
    Given a basic test agent setup
    When I call dispose on an agent with disposable streams
    Then both input and output streams should be disposed if available


  Scenario: AgentWithMemory stores and recalls values with its lock
    Given a basic test agent setup
    When I remember a value and recall it later
    Then the remembered value should be returned from memory


  # ============================================================
  # Originally from: bridge_coverage.feature
  # Feature: RxPyLangGraphBridge uncovered lines and branches
  # ============================================================

  Scenario: __del__ suppresses AttributeError when cleanup_tasks is unavailable
    Given a bridge with its cleanup_tasks method removed
    When __del__ is invoked on the crippled bridge
    Then no exception should propagate from __del__


  Scenario: cleanup_tasks_async cancels tasks that are still running
    Given a bridge with a long-running async task
    When I run cleanup_tasks_async on the bridge
    Then the running task should be cancelled
    And the bridge active tasks set should be empty


  Scenario: Graph executor processes a string message through execute_graph
    Given a bridge with a mock graph that returns messages
    When I run the graph executor with a string content message
    Then the executor result should contain the graph output
    And the result metadata should include graph execution history


  Scenario: Graph executor processes a dict message through execute_graph
    Given a bridge with a mock graph that returns messages
    When I run the graph executor with a dict content message
    Then the executor result should contain the graph output


  Scenario: Graph executor processes a non-str non-dict message through execute_graph
    Given a bridge with a mock graph that returns messages
    When I run the graph executor with a numeric content message
    Then the executor result should contain the graph output


  Scenario: Graph executor falls back to state dict when messages list is empty
    Given a bridge with a mock graph that returns empty messages
    When I run the graph executor with a string content message
    Then the executor result should be the full state dict


  Scenario: create_graph_stream returns StreamConfig for a known graph
    Given a bridge that owns a registered graph named "demo"
    When I call create_graph_stream with "demo"
    Then the returned StreamConfig name should be "graph_demo"


  Scenario: _create_state_checkpointer succeeds for a valid graph name
    Given a bridge that owns a registered graph named "ckpt_graph"
    When I build a state checkpointer for "ckpt_graph"
    Then the checkpointer operator should be returned without error


  # ============================================================
  # Originally from: coverage_improvements.feature
  # Feature: Improve Code Coverage
  # ============================================================

  @coverage
  Scenario: Test __main__ module direct execution
    When I test the __main__ module if name equals main block
    Then the sys.exit should be called with the run return value


  @coverage
  Scenario: Test platform module when module needs importing
    Given I remove "cleveragents.cli" from sys.modules
    When I test ensure_cli_importable with missing module
    Then the module should be freshly imported


  @coverage
  @tdd_issue @tdd_issue_4240 @tdd_expected_fail
  Scenario: Test CLI main function pass statement
    When I call the CLI main function with no commands
    Then the function should complete without error


  @coverage
  Scenario: Test exit code normalization with negative code
    Given the CLI returns a negative exit code -5
    When I normalize the exit code
    Then the normalized code should be -5


  @coverage
  Scenario: Test exit code normalization with code over 255
    Given the CLI returns an exit code 300
    When I normalize the exit code
    Then the normalized code should be 255


  @coverage
  Scenario: Test exit code normalization with None returning 0
    Given the CLI returns None as exit code
    When I normalize the exit code
    Then the normalized code should be 0


  @coverage
  Scenario: Test exit code normalization with invalid string
    Given the CLI returns "invalid" as exit code
    When I normalize the exit code
    Then the normalized code should be 1

  # ============================================================
  # Originally from: m6_autonomy_acceptance.feature
  # Feature: M6 autonomy acceptance smoke tests
  # ============================================================

  Scenario: M6 smoke load A2A facade flows fixture
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke load the A2A facade flows fixture
    Then the m6 smoke facade fixture should have a session lifecycle entry
    And the m6 smoke facade fixture should have a plan lifecycle entry


  Scenario: M6 smoke load autonomy guardrails fixture
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke load the autonomy guardrails fixture
    Then the m6 smoke guardrails fixture should have a denylist entry
    And the m6 smoke guardrails fixture should have an allowlist entry
    And the m6 smoke guardrails fixture should have a cost budget entry


  Scenario: M6 smoke load automation profiles fixture
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke load the automation profiles fixture
    Then the m6 smoke profiles fixture should list all 8 built-in names
    And the m6 smoke profiles fixture should have a custom profile entry

    # --- A2A facade session operations ---


  Scenario: M6 smoke A2A session create returns session id
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "session.create" with params {}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data should contain key "session_id"
    And the m6 smoke response data should contain key "status"


  Scenario: M6 smoke A2A session close returns closed status
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "session.close" with params {}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data "status" should equal "closed"

    # --- A2A facade plan operations ---


  Scenario: M6 smoke A2A plan create returns plan id
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "plan.create" with params {}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data should contain key "plan_id"


  Scenario: M6 smoke A2A plan execute queues plan
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "plan.execute" with params {"plan_id": "01M6SM0KE00000000000000001"}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data "status" should equal "queued"


  Scenario: M6 smoke A2A plan status returns phase
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "plan.status" with params {"plan_id": "01M6SM0KE00000000000000001"}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data should contain key "phase"


  Scenario: M6 smoke A2A plan diff returns changes list
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "plan.diff" with params {"plan_id": "01M6SM0KE00000000000000001"}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data should contain key "changes"


  Scenario: M6 smoke A2A plan apply returns applied status
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "plan.apply" with params {"plan_id": "01M6SM0KE00000000000000001"}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data "status" should equal "applied"

    # --- A2A facade registry and context operations ---


  Scenario: M6 smoke A2A registry list tools returns empty list
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "registry.list_tools" with params {}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data should contain key "tools"


  Scenario: M6 smoke A2A registry list resources returns empty list
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "registry.list_resources" with params {}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data should contain key "resources"


  Scenario: M6 smoke A2A context get returns context dict
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "context.get" with params {}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data should contain key "context"


  Scenario: M6 smoke A2A event subscribe returns subscription id
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch "event.subscribe" with params {}
    Then the m6 smoke response status should be "ok"
    And the m6 smoke response data should contain key "subscription_id"

    # --- A2A facade error handling ---


  Scenario: M6 smoke A2A unknown operation raises error
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch unknown operation "nonexistent.op"
    Then the m6 smoke facade should raise A2aOperationNotFoundError


  Scenario: M6 smoke A2A dispatch with invalid request type raises TypeError
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke dispatch with a non-A2aRequest object
    Then the m6 smoke facade should raise TypeError

    # --- A2A facade service registration ---


  Scenario: M6 smoke A2A register service stores service
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke register service "plan_service" on the facade
    Then the m6 smoke facade should have service "plan_service"


  Scenario: M6 smoke A2A list operations returns all supported
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke list facade operations
    Then the m6 smoke operations should include "session.create"
    And the m6 smoke operations should include "plan.execute"
    And the m6 smoke operations should include "event.subscribe"
    And the m6 smoke operations count should be 42

    # --- A2A event queue ---


  Scenario: M6 smoke A2A event queue publish and retrieve
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke A2A event queue
    When I m6 smoke publish an event with type "plan.progress"
    Then the m6 smoke event queue should have 1 event
    And the m6 smoke last event type should be "plan.progress"


  Scenario: M6 smoke A2A event queue subscribe local callback
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke A2A event queue
    When I m6 smoke subscribe a local callback
    And I m6 smoke publish an event with type "plan.complete"
    Then the m6 smoke callback should have been called once


  Scenario: M6 smoke A2A event queue unsubscribe
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke A2A event queue
    When I m6 smoke subscribe a local callback
    And I m6 smoke unsubscribe the callback
    And I m6 smoke publish an event with type "plan.complete"
    Then the m6 smoke callback should not have been called


  Scenario: M6 smoke A2A event queue close prevents publish
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke A2A event queue
    When I m6 smoke close the event queue
    Then the m6 smoke publishing should raise RuntimeError


  Scenario: M6 smoke A2A event queue remote subscribe raises error
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke A2A event queue
    When I m6 smoke attempt remote subscribe to "https://example.com/events"
    Then the m6 smoke facade should raise A2aNotAvailableError

    # --- A2A HTTP transport stub ---


  Scenario: M6 smoke A2A transport send raises not available
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke attempt transport send
    Then the m6 smoke facade should raise A2aNotAvailableError


  Scenario: M6 smoke A2A transport connect raises not available
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke attempt transport connect to "https://example.com/a2a"
    Then the m6 smoke facade should raise A2aNotAvailableError


  Scenario: M6 smoke A2A transport disconnect raises not available
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke attempt transport disconnect
    Then the m6 smoke facade should raise A2aNotAvailableError


  Scenario: M6 smoke A2A transport is_connected returns false
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke check transport is_connected
    Then the m6 smoke transport should not be connected

    # --- A2A version negotiation ---


  Scenario: M6 smoke A2A version negotiation accepts 2.0
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke negotiate A2A version "2.0"
    Then the m6 smoke negotiated version should be "2.0"


  Scenario: M6 smoke A2A version negotiation rejects unsupported
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke negotiate A2A version "99.0"
    Then the m6 smoke facade should raise A2aVersionMismatchError


  Scenario: M6 smoke A2A version is_supported returns correct result
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke check if version "2.0" is supported
    Then the m6 smoke version support should be true
    When I m6 smoke check if version "99.0" is supported
    Then the m6 smoke version support should be false

    # --- Automation profiles built-in ---


  Scenario: M6 smoke all 8 built-in profiles exist
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke list all built-in profiles
    Then the m6 smoke profile count should be 8
    And the m6 smoke profiles should include "manual"
    And the m6 smoke profiles should include "full-auto"


  Scenario: M6 smoke manual profile has all thresholds at 1.0
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke load built-in profile "manual"
    Then the m6 smoke profile decompose_task should be 1.0
    And the m6 smoke profile create_tool should be 1.0
    And the m6 smoke profile select_tool should be 1.0
    And the m6 smoke profile require_sandbox should be true


  Scenario: M6 smoke full-auto profile has no gates
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke load built-in profile "full-auto"
    Then the m6 smoke profile decompose_task should be 0.0
    And the m6 smoke profile create_tool should be 0.0
    And the m6 smoke profile select_tool should be 0.0
    And the m6 smoke profile require_sandbox should be false
    And the m6 smoke profile allow_unsafe_tools should be true

    # --- Automation profile creation and validation ---


  Scenario: M6 smoke create custom namespaced profile
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke create a profile named "acme/strict" with select_tool 1.0
    Then the m6 smoke created profile name should be "acme/strict"
    And the m6 smoke created profile select_tool should be 1.0


  Scenario: M6 smoke profile name validation rejects invalid
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke create a profile with invalid name "has spaces"
    Then the m6 smoke creation should raise ValueError


  Scenario: M6 smoke profile threshold validation rejects out of range
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke create a profile with decompose_task 1.5
    Then the m6 smoke creation should raise ValueError


  Scenario: M6 smoke profile from_yaml loads correctly
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke temporary profile YAML file
    When I m6 smoke load profile from the temp YAML
    Then the m6 smoke loaded profile name should be "test-yaml-profile"

    # --- Guard enforcement ---


  Scenario: M6 smoke guard denylist blocks denied tool
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke profile with denylist guard for "rm_rf"
    When I m6 smoke check guard for tool "rm_rf"
    Then the m6 smoke guard result should not be allowed
    And the m6 smoke guard reason should contain "denylist"


  Scenario: M6 smoke guard denylist allows non-denied tool
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke profile with denylist guard for "rm_rf"
    When I m6 smoke check guard for tool "read_file"
    Then the m6 smoke guard result should be allowed


  Scenario: M6 smoke guard allowlist blocks unlisted tool
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke profile with allowlist guard for "read_file" and "search"
    When I m6 smoke check guard for tool "write_file"
    Then the m6 smoke guard result should not be allowed
    And the m6 smoke guard reason should contain "allowlist"


  Scenario: M6 smoke guard max tool calls blocks at limit
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke profile with max 5 tool calls per step
    When I m6 smoke check guard for tool "llm_call" with 5 calls so far
    Then the m6 smoke guard result should not be allowed
    And the m6 smoke guard reason should contain "limit"


  Scenario: M6 smoke guard cost budget blocks at cap
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke profile with max cost 10.0
    When I m6 smoke check guard for tool "llm_call" with cost 10.0
    Then the m6 smoke guard result should not be allowed
    And the m6 smoke guard reason should contain "budget"


  Scenario: M6 smoke guard write approval blocks write operations
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke profile with write approval required
    When I m6 smoke check guard for tool "write_file" as a write operation
    Then the m6 smoke guard result should not be allowed
    And the m6 smoke guard reason should contain "Write operations"


  Scenario: M6 smoke guard apply approval blocks apply phase
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke profile with apply approval required
    When I m6 smoke check guard for tool "__apply__"
    Then the m6 smoke guard result should not be allowed
    And the m6 smoke guard reason should contain "Apply phase"


  Scenario: M6 smoke guard with no guards allows everything
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke profile with no guards
    When I m6 smoke check guard for tool "anything"
    Then the m6 smoke guard result should be allowed

    # --- Profile service resolution precedence ---


  Scenario: M6 smoke profile resolution plan takes precedence
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke automation profile service
    When I m6 smoke resolve profile with plan "ci" action "auto" project "manual"
    Then the m6 smoke resolved profile name should be "ci"


  Scenario: M6 smoke profile resolution action takes precedence over project
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke automation profile service
    When I m6 smoke resolve profile with plan null action "auto" project "manual"
    Then the m6 smoke resolved profile name should be "auto"


  Scenario: M6 smoke profile resolution falls back to global default
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke automation profile service
    When I m6 smoke resolve profile with plan null action null project null
    Then the m6 smoke resolved profile name should be "manual"

    # --- Profile service guard evaluation ---


  Scenario: M6 smoke service evaluate guard delegates to profile
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    Given a m6 smoke automation profile service
    When I m6 smoke evaluate guard for profile "manual" and tool "read_file"
    Then the m6 smoke guard result should be allowed

    # --- A2A model validation ---


  Scenario: M6 smoke A2aRequest validates non-empty operation
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke create A2aRequest with empty operation
    Then the m6 smoke creation should raise ValueError


  Scenario: M6 smoke A2aResponse validates status values
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke create A2aResponse with invalid status "maybe"
    Then the m6 smoke creation should raise ValueError


  Scenario: M6 smoke A2aEvent validates non-empty event_type
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke create A2aEvent with empty event_type
    Then the m6 smoke creation should raise ValueError


  Scenario: M6 smoke A2aErrorDetail validates non-empty fields
    Given a m6 smoke test runner
    And a m6 smoke A2A local facade
    When I m6 smoke create A2aErrorDetail with empty code
    Then the m6 smoke creation should raise ValueError


  # ============================================================
  # Originally from: scale_test.feature
  # Feature: Scale test fixtures and performance baselines
  # ============================================================

  Scenario: Load scale metadata fixture from disk
    Given a scale test environment is ready
    When I scale test load the scale metadata fixture
    Then the scale test metadata should contain a schema version
    And the scale test metadata should contain scale profiles


  Scenario: Scale metadata contains all required profiles
    Given a scale test environment is ready
    When I scale test load the scale metadata fixture
    Then the scale test metadata should have exactly 5 profiles
    And the scale test profile names should be "small, medium, large, xlarge, xxlarge"


  Scenario: Each scale profile has required fields
    Given a scale test environment is ready
    When I scale test load the scale metadata fixture
    Then each scale test profile should have a file count
    And each scale test profile should have a total size in megabytes
    And each scale test profile should have a language mix
    And each scale test profile should have expected index time
    And each scale test profile should have expected decomposition time


  Scenario: Scale metadata lists supported languages
    Given a scale test environment is ready
    When I scale test load the scale metadata fixture
    Then the scale test metadata should list supported languages
    And the scale test supported languages should include "python"
    And the scale test supported languages should include "javascript"

    # ──────────────────────────────────────────────────
    # Section 2: Threshold matrix validation
    # ──────────────────────────────────────────────────


  Scenario: Load baseline thresholds fixture from disk
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then the scale test thresholds should contain a schema version
    And the scale test thresholds should have indexing section
    And the scale test thresholds should have decomposition section


  Scenario: Indexing thresholds cover all file counts
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then the scale test indexing thresholds should cover "1000_files"
    And the scale test indexing thresholds should cover "5000_files"
    And the scale test indexing thresholds should cover "10000_files"
    And the scale test indexing thresholds should cover "50000_files"
    And the scale test indexing thresholds should cover "100000_files"


  Scenario: Each indexing threshold has percentile fields
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then each scale test indexing threshold should have p50 p95 and p99


  Scenario: Decomposition thresholds cover all file counts
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then the scale test decomposition thresholds should cover "1000_files"
    And the scale test decomposition thresholds should cover "5000_files"
    And the scale test decomposition thresholds should cover "10000_files"
    And the scale test decomposition thresholds should cover "50000_files"
    And the scale test decomposition thresholds should cover "100000_files"


  Scenario: Each decomposition threshold has percentile fields
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then each scale test decomposition threshold should have p50 p95 and p99


  Scenario: Thresholds are monotonically increasing across percentiles
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then scale test indexing p50 should be less than p95 for all file counts
    And scale test indexing p95 should be less than p99 for all file counts

    # ──────────────────────────────────────────────────
    # Section 3: Language mix distribution validation
    # ──────────────────────────────────────────────────


  Scenario: Language mix sums to 1.0 for each profile
    Given a scale test environment is ready
    When I scale test load the scale metadata fixture
    Then the scale test language mix should sum to 1.0 for each profile


  Scenario: Language mix values are between 0 and 1
    Given a scale test environment is ready
    When I scale test load the scale metadata fixture
    Then each scale test language mix value should be between 0 and 1


  Scenario: All languages in mix are in supported languages list
    Given a scale test environment is ready
    When I scale test load the scale metadata fixture
    Then all scale test language mix keys should be in supported languages

    # ──────────────────────────────────────────────────
    # Section 4: Scale profile simulation
    # ──────────────────────────────────────────────────


  Scenario: Generate file count distribution for small profile
    Given a scale test environment is ready
    When I scale test generate file distribution for the "small" profile
    Then the scale test total generated file count should be 1000
    And the scale test generated distribution should match the language mix


  Scenario: Generate file count distribution for medium profile
    Given a scale test environment is ready
    When I scale test generate file distribution for the "medium" profile
    Then the scale test total generated file count should be 5000
    And the scale test generated distribution should match the language mix


  Scenario: Generate file count distribution for large profile
    Given a scale test environment is ready
    When I scale test generate file distribution for the "large" profile
    Then the scale test total generated file count should be 10000
    And the scale test generated distribution should match the language mix

    # ──────────────────────────────────────────────────
    # Section 5: Baseline threshold realism checks
    # ──────────────────────────────────────────────────


  Scenario: Indexing thresholds scale sub-linearly with file count
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then scale test 10K indexing p50 should be less than 10x the 1K p50


  Scenario: Decomposition thresholds exceed indexing thresholds
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then scale test decomposition p50 should be greater than indexing p50 for each file count


  Scenario: Memory thresholds are present and realistic
    Given a scale test environment is ready
    When I scale test load the baseline thresholds fixture
    Then the scale test thresholds should have memory usage section
    And scale test memory peak should exceed steady state for each file count


  # ============================================================
  # Originally from: container_coverage_r2.feature
  # Feature: Container module coverage round 2
  # ============================================================

  Scenario: AI provider returns None when mock disabled and no providers configured
    Given r2cont- the mock AI environment flag is disabled
    And r2cont- a provider registry with no configured providers
    When r2cont- I call get_ai_provider with the mocked registry
    Then r2cont- the ai provider result should be None


  Scenario: AI provider returns real provider when mock disabled and providers exist
    Given r2cont- the mock AI environment flag is disabled
    And r2cont- a provider registry with configured providers
    When r2cont- I call get_ai_provider with the mocked registry
    Then r2cont- the ai provider result should not be None

    # -------------------------------------------------------------------
    # get_ai_provider – features path already on sys.path (line 57 partial)
    # -------------------------------------------------------------------


  Scenario: AI provider with features path already on sys.path does not duplicate it
    Given r2cont- the mock AI environment flag is enabled
    And r2cont- the features path is already on sys.path
    When r2cont- I call get_ai_provider without registry args
    Then r2cont- the features path should appear exactly once on sys.path

    # -------------------------------------------------------------------
    # _build_project_resource_link_repo (lines 125-130)
    # -------------------------------------------------------------------


  Scenario: Build project resource link repo creates valid repository
    When r2cont- I build a project resource link repo with an in-memory database
    Then r2cont- the result should be a ProjectResourceLinkRepository instance

    # -------------------------------------------------------------------
    # get_database_url – env var branches (lines 82, 87)
    # -------------------------------------------------------------------


  Scenario: get_database_url returns fallback when no env vars are set
    Given r2cont- no database URL environment variables are set
    When r2cont- I call get_database_url
    Then r2cont- the database URL should start with "sqlite:///"


  Scenario: get_database_url returns CLEVERAGENTS_DATABASE_URL when set
    Given r2cont- the env var "CLEVERAGENTS_DATABASE_URL" is set to "sqlite:///explicit.db"
    When r2cont- I call get_database_url
    Then r2cont- the database URL should be "sqlite:///explicit.db"


  Scenario: get_database_url returns CLEVERAGENTS_TEST_DATABASE_URL when primary is unset
    Given r2cont- the env var "CLEVERAGENTS_DATABASE_URL" is cleared
    And r2cont- the env var "CLEVERAGENTS_TEST_DATABASE_URL" is set to "sqlite:///test.db"
    When r2cont- I call get_database_url
    Then r2cont- the database URL should be "sqlite:///test.db"


  Scenario: get_database_url skips empty env var values
    Given r2cont- the env var "CLEVERAGENTS_DATABASE_URL" is set to empty
    And r2cont- the env var "CLEVERAGENTS_TEST_DATABASE_URL" is set to empty
    When r2cont- I call get_database_url
    Then r2cont- the database URL should start with "sqlite:///"

    # -------------------------------------------------------------------
    # get_container – cached instance (line 256 False branch)
    # -------------------------------------------------------------------


  Scenario: get_container returns the cached instance on subsequent calls
    Given r2cont- the global container has been reset
    When r2cont- I call get_container twice
    Then r2cont- both calls should return the same container instance

    # -------------------------------------------------------------------
    # override_providers – non-existent attribute (line 285 False)
    # -------------------------------------------------------------------


  Scenario: override_providers ignores non-existent provider names
    Given r2cont- the global container has been reset
    When r2cont- I override a provider with a non-existent name
    Then r2cont- no error should have been raised

    # -------------------------------------------------------------------
    # override_providers – non-database_url provider (line 288 else)
    # -------------------------------------------------------------------


  Scenario: override_providers handles non-database_url providers via Object
    Given r2cont- the global container has been reset
    When r2cont- I override the settings provider with a mock value
    Then r2cont- the settings provider should return the mock value


  # ============================================================
  # Originally from: lsp_registry.feature
  # Feature: LSP Registry and Runtime Stubs
  # ============================================================

  @lsp_reg_create
  Scenario: Register a new LSP server config
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    When the LSP server is registered
    Then the LSP registry should contain "local/pyright"
    And the LSP registry should have 1 server


  @lsp_reg_create
  Scenario: Register LSP server with multiple languages
    Given a clean LSP registry
    Given an LSP server config named "local/tsserver" for languages "typescript,javascript" with command "typescript-language-server"
    When the LSP server is registered
    Then the LSP registry should contain "local/tsserver"
    And the LSP server "local/tsserver" should support language "typescript"
    And the LSP server "local/tsserver" should support language "javascript"


  @lsp_reg_create
  Scenario: Register LSP server with capabilities
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    And the LSP server config has capabilities "diagnostics,completions,hover"
    When the LSP server is registered
    Then the LSP server "local/pyright" should have 3 capabilities


  @lsp_reg_create @error_handling
  Scenario: Registering a duplicate server name is rejected
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    And the LSP server is registered
    When a second LSP server with the same name "local/pyright" is registered
    Then a ValueError should be raised mentioning "already registered"

    # ---------------------------------------------------------------------------
    # Lookup by name
    # ---------------------------------------------------------------------------


  @lsp_reg_get
  Scenario: Lookup a registered server by name
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    And the LSP server is registered
    When I look up LSP server "local/pyright"
    Then the LSP lookup result should not be None
    And the LSP lookup result name should be "local/pyright"


  @lsp_reg_get
  Scenario: Lookup a non-existent server returns None
    Given a clean LSP registry
    When I look up LSP server "local/nonexistent"
    Then the LSP lookup result should be None


  @lsp_reg_get @error_handling
  Scenario: Lookup with get_or_raise for missing server raises error
    Given a clean LSP registry
    When I look up LSP server "local/missing" with get_or_raise
    Then an LspServerNotFoundError should be raised for "local/missing"

    # ---------------------------------------------------------------------------
    # List with namespace filter
    # ---------------------------------------------------------------------------


  @lsp_reg_list
  Scenario: List all registered servers
    Given a clean LSP registry
    Given the following LSP servers are registered:
      | name             | language | command                    |
      | local/pyright    | python   | pyright-langserver         |
      | local/tsserver   | typescript | typescript-language-server |
      | devops/clangd    | c        | clangd                     |
    When I list all LSP servers
    Then the LSP server list should contain 3 servers


  @lsp_reg_list
  Scenario: List servers filtered by namespace
    Given a clean LSP registry
    Given the following LSP servers are registered:
      | name             | language | command                    |
      | local/pyright    | python   | pyright-langserver         |
      | local/tsserver   | typescript | typescript-language-server |
      | devops/clangd    | c        | clangd                     |
    When I list LSP servers with namespace "local"
    Then the LSP server list should contain 2 servers

    # ---------------------------------------------------------------------------
    # List with language filter
    # ---------------------------------------------------------------------------


  @lsp_reg_list
  Scenario: List servers filtered by language
    Given a clean LSP registry
    Given the following LSP servers are registered:
      | name             | language | command                    |
      | local/pyright    | python   | pyright-langserver         |
      | local/tsserver   | typescript | typescript-language-server |
      | devops/pylsp     | python   | pylsp                      |
    When I list LSP servers with language "python"
    Then the LSP server list should contain 2 servers

    # ---------------------------------------------------------------------------
    # Remove server
    # ---------------------------------------------------------------------------


  @lsp_reg_remove
  Scenario: Remove a registered server
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    And the LSP server is registered
    When I remove LSP server "local/pyright"
    Then the LSP removal result should be True
    And the LSP registry should have 0 servers


  @lsp_reg_remove
  Scenario: Remove a non-existent server returns False
    Given a clean LSP registry
    When I remove LSP server "local/nonexistent"
    Then the LSP removal result should be False

    # ---------------------------------------------------------------------------
    # Runtime raises LspServerNotFoundError for unregistered servers
    # ---------------------------------------------------------------------------


  @lsp_runtime
  Scenario: Runtime start_server raises error for unregistered server
    Given a clean LSP registry
    Given a local-mode LSP runtime
    When I call start_server on the runtime with name "local/pyright" and workspace "/tmp"
    Then an LSP error should be raised


  @lsp_runtime
  Scenario: Runtime stop_server raises error for unregistered server
    Given a clean LSP registry
    Given a local-mode LSP runtime
    When I call stop_server on the runtime with name "local/pyright"
    Then an LSP error should be raised


  @lsp_runtime
  Scenario: Runtime get_diagnostics raises error for unregistered server
    Given a clean LSP registry
    Given a local-mode LSP runtime
    When I call get_diagnostics on the runtime with name "local/pyright" and file "/tmp/test.py"
    Then an LSP error should be raised


  @lsp_runtime
  Scenario: Runtime get_completions raises error for unregistered server
    Given a clean LSP registry
    Given a local-mode LSP runtime
    When I call get_completions on the runtime with name "local/pyright" file "/tmp/test.py" line 1 column 1
    Then an LSP error should be raised

    # ---------------------------------------------------------------------------
    # Tool adapter generates tool specs from capabilities
    # ---------------------------------------------------------------------------


  @lsp_tool_adapter
  Scenario: Tool adapter generates specs for each capability
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    And the LSP server config has capabilities "diagnostics,completions"
    When I generate tool specs from the adapter
    Then the tool spec list should contain 2 specs
    And the tool spec names should include "local/pyright/diagnostics"
    And the tool spec names should include "local/pyright/completions"


  @lsp_tool_adapter
  Scenario: Tool adapter handler raises LspNotAvailableError in local mode
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    And the LSP server config has capabilities "diagnostics"
    When I generate tool specs from the adapter
    And I invoke the first tool spec handler
    Then an LspNotAvailableError should be raised

    # ---------------------------------------------------------------------------
    # Binding validation (valid/invalid references)
    # ---------------------------------------------------------------------------


  @lsp_binding
  Scenario: Valid binding with registered server passes
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    And the LSP server is registered
    When I create a binding for node "code-editor" to server "local/pyright"
    Then the binding should be valid
    And the binding node_name should be "code-editor"
    And the binding lsp_server_name should be "local/pyright"


  @lsp_binding @error_handling
  Scenario: Binding with non-namespaced server name is rejected
    Given a clean LSP registry
    When I create a binding for node "code-editor" to server "pyright"
    Then a ValidationError should be raised mentioning "namespaced"


  @lsp_binding
  Scenario: Binding auto_detect defaults to True
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    And the LSP server is registered
    When I create a binding for node "code-editor" to server "local/pyright"
    Then the binding auto_detect should be True

    # ---------------------------------------------------------------------------
    # Model validation
    # ---------------------------------------------------------------------------


  @lsp_model_validation
  Scenario: LspServerConfig rejects non-namespaced name
    Given a clean LSP registry
    When I try to create an LSP server config with name "pyright" and command "pyright-langserver"
    Then a ValidationError should be raised mentioning "namespaced"


  @lsp_model_validation
  Scenario: LspServerConfig normalizes language strings to lowercase
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "Python" with command "pyright-langserver"
    Then the LSP server config language "python" should be present


  @lsp_model_validation
  Scenario: LspServerConfig namespace property extracts prefix
    Given a clean LSP registry
    Given an LSP server config named "local/pyright" for language "python" with command "pyright-langserver"
    Then the LSP server config namespace should be "local"
    And the LSP server config short_name should be "pyright"

    # ---------------------------------------------------------------------------
    # Error hierarchy
    # ---------------------------------------------------------------------------


  @lsp_errors
  Scenario: LspError inherits from CleverAgentsError
    Given a clean LSP registry
    Then LspError should be a subclass of CleverAgentsError


  @lsp_errors
  Scenario: LspNotAvailableError has default message
    Given a clean LSP registry
    When I create an LspNotAvailableError with default message
    Then the LSP error message should contain "not available in local mode"


  @lsp_errors
  Scenario: LspServerNotFoundError includes server name
    Given a clean LSP registry
    When I create an LspServerNotFoundError for server "local/pyright"
    Then the LSP error message should contain "local/pyright"
    And the error server_name should be "local/pyright"


  @lsp_errors @error_handling
  Scenario: LspServerNotFoundError rejects empty server name
    Given a clean LSP registry
    When I try to create an LspServerNotFoundError with empty name
    Then a ValueError should be raised


  # ============================================================
  # Originally from: memory_service_coverage.feature
  # Feature: Memory Service Coverage
  # ============================================================

  Scenario: Conversation adapter returns buffer text when configured for summaries
    Given a conversation adapter configured to return text values
    When I load memory variables from the adapter
    Then the adapter provides a string buffer representation


  Scenario: Conversation adapter handles nested context and async operations
    Given a conversation adapter with prune tracking
    When I save nested user and ai context through the adapter
    And I invoke the adapter async operations
    Then the adapter normalizes all values and prunes history three times


  Scenario: Memory service prunes history to the configured window
    Given a memory service limited to 3 messages
    When I add five interactions via save context
    And I ask for the two most recent messages
    And I ask for more messages than available
    Then the service keeps only the three latest messages
    And the recent message requests respect their sizes
    And the service summary honors a 20 character limit
    And memory variables expose history and counts


  Scenario: Memory service manages direct messages and adapters
    Given a memory service with default settings
    When I add direct message objects to the service
    And I inspect the default conversation adapter
    And I reset the max message limit to 1
    And I save a new interaction to enforce pruning
    And I clear the service memory
    Then the service computed token counts and summaries before clearing
    And the service history is empty after clearing
    And a custom conversation adapter returns text values


  Scenario: Memory service uses SQL chat history when available
    Given a patched SQL chat history for memory service
    When I create a memory service with a connection string
    Then the SQL history constructor receives the session and connection
    And stored messages flow through the patched SQL history

    # ==================== EntityMemory Tests ====================


  Scenario: Track a new entity
    Given a memory service with entity tracking enabled
    When I track a project entity named "my-project"
    Then the entity should be stored successfully
    And the entity should have type "project"
    And the entity mention count should be 1


  Scenario: Track entity with metadata
    Given a memory service with entity tracking enabled
    When I track a plan entity named "feature-plan" with metadata
    Then the entity metadata should include the provided values
    And the entity should be retrievable by name and type


  Scenario: Update existing entity increments mention count
    Given a memory service with entity tracking enabled
    And I have tracked a file entity named "main.py"
    When I track the same entity again
    Then the entity mention count should be 2
    And the entity last_seen should be updated


  Scenario: Get entities by type
    Given a memory service with multiple entities tracked
    When I get all entities of type "project"
    Then I should receive only project entities
    And I should not receive entities of other types


  Scenario: Get all entities
    Given a memory service with multiple entities tracked
    When I get all tracked entities
    Then I should receive all entities regardless of type


  Scenario: Get recent entities
    Given a memory service with multiple entities tracked over time
    When I get the 3 most recent entities
    Then I should receive entities sorted by last access time
    And I should receive at most 3 entities


  Scenario: Search entities by name
    Given a memory service with multiple entities tracked
    When I search for entities with "main" in the name
    Then I should receive entities whose names contain "main"
    And the search should be case-insensitive


  Scenario: Remove specific entity
    Given a memory service with entity tracking enabled
    And I have tracked a context entity named "src/utils.py"
    When I remove the entity
    Then the entity should no longer exist
    And attempting to get the removed entity should return None


  Scenario: Clear all entities
    Given a memory service with multiple entities tracked
    When I clear all entities
    Then the entity store should be empty
    And get_entities should return an empty list


  Scenario: Clear entities by type
    Given a memory service with multiple entities tracked
    When I clear only project entities
    Then project entities should be removed
    And entities of other types should remain


  Scenario: Get entity summary
    Given a memory service with multiple entities tracked
    When I get the entity summary
    Then the summary should include total entity count
    And the summary should include counts by type
    And the summary should include recent entities


  Scenario: Entity type can be specified as string
    Given a memory service with entity tracking enabled
    When I track an entity with type specified as string "custom"
    Then the entity should be tracked with EntityType.CUSTOM
    And the entity should be retrievable using either string or enum type


  Scenario: Entity store is accessible directly
    Given a memory service with entity tracking enabled
    When I access the entity_store property
    Then I should receive the underlying EntityStore instance
    And I can track entities through it directly

    # ==================== Coverage Enhancement Tests ====================


  Scenario: Entity from_dict deserializes entity from dictionary
    Given an entity dictionary with valid data
    When I create an Entity from the dictionary using from_dict
    Then the Entity should have the correct name and type
    And the Entity should have the correct metadata
    And the Entity should have the correct timestamps
    And the Entity should have the correct mention count


  Scenario: Updating existing entity also updates metadata
    Given a memory service with entity tracking enabled
    And I have tracked a project entity named "metadata-project" with initial metadata
    When I track the same project entity again with additional metadata
    Then the entity metadata should contain both initial and additional values
    And the entity mention count should be 2


  Scenario: Remove non-existent entity returns False
    Given a memory service with entity tracking enabled
    When I attempt to remove an entity that does not exist
    Then the remove operation should return False


  Scenario: EntityStore with connection string triggers persistence
    Given an entity store with a mock connection string
    When I track an entity to trigger dirty state
    Then the persist callback should mark the store as not dirty
    And subsequent reads should find the entity


  Scenario: Get summary returns empty string for empty conversation
    Given a memory service with no messages
    When I request the conversation summary
    Then the summary should be an empty string


  # ============================================================
  # Originally from: migration_runner_coverage.feature
  # Feature: Migration runner coverage
  # ============================================================

  Scenario: Missing Alembic config raises helpful error
    Given a migration runner configured for "sqlite:///:memory:"
    When I attempt to load the alembic config without an alembic.ini file
    Then a FileNotFoundError should be raised mentioning "alembic.ini"


  Scenario: Running migrations without engine uses Alembic-managed connection
    Given a migration runner configured for "sqlite:///:memory:"
    When I run migrations without providing an engine
    Then the upgrade command should be invoked without a connection attribute
    And the CLEVERAGENTS_DATABASE_URL environment variable should be restored


  Scenario: In-memory SQLite initialization reuses cached engine
    Given a migration runner configured for "sqlite:///:memory:"
    When I initialize or upgrade the database with cached in-memory engine
    Then migrations should run using the cached engine connection
    And the cached engine should remain available without disposal


  Scenario: Legacy database is stamped when tables exist without alembic_version
    Given a migration runner configured for "postgresql://user:pass@localhost/testdb"
    When I initialize or upgrade the database with legacy tables present
    Then the stamp command should run using the active connection
    And the external database engine should be disposed after initialization


  Scenario: Pending migrations include newest revisions when no current version exists
    Given a migration runner configured for "sqlite:///:memory:"
    When I request pending migrations for a database with no current revision
    Then the pending migration list should be ordered from oldest to newest


  Scenario: Get current revision queries migration context
    Given a migration runner configured for "sqlite:///:memory:"
    When I request the current revision from the database
    Then the migration context should be queried for the current revision
    And the temporary connection should be closed afterward


  Scenario: File-based SQLite database directory is created if missing
    Given a migration runner configured for "sqlite:///tmp/test-db/mydb.db"
    When I initialize or upgrade a file-based SQLite database
    Then the parent directory should be created if it does not exist
    And the database engine should be disposed after initialization


  Scenario: File-based SQLite uses engine with proper connection args
    Given a migration runner configured for "sqlite:///tmp/test-db2/mydb.db"
    When I initialize or upgrade a file-based SQLite database
    Then the engine should be created with check_same_thread set to False
    And the database engine should be disposed after initialization


  Scenario: Running migrations when database already up to date
    Given a migration runner configured for "sqlite:///:memory:"
    When I initialize the database and migrations are already applied
    Then no additional migrations should be run
    And check migrations needed should return False


  Scenario: Pending migrations trigger upgrade when alembic_version exists
    Given a migration runner configured for "sqlite:///:memory:"
    When I initialize the database with pending migrations detected
    Then run migrations should be invoked with the existing engine
    And the in-memory engine should not be disposed


  Scenario: Pending migrations require approval before running
    Given a migration runner configured for "sqlite:///:memory:"
    When I initialize the database with pending migrations requiring confirmation
    Then the migration approval prompt should be invoked
    And migrations should run after approval


  Scenario: Declining migration approval raises an error
    Given a migration runner configured for "sqlite:///:memory:"
    When I decline migration approval when pending migrations exist
    Then a migration approval error should be raised


  Scenario: Default prompt auto-approves when running in CI
    Given a migration runner configured for "sqlite:///:memory:"
    When I evaluate the default migration prompt while CI is set
    Then the default prompt should auto approve without interaction


  Scenario: Default prompt delegates to typer confirm in TTY
    Given a migration runner configured for "sqlite:///:memory:"
    When I evaluate the default migration prompt in an interactive TTY
    Then the default prompt should return the typer confirmation result


  Scenario: Default prompt auto-approves after interactive prompt failure
    Given a migration runner configured for "sqlite:///:memory:"
    When the default migration prompt raises an error while prompting
    Then the default prompt should auto approve after the failure


  # ============================================================
  # Originally from: system_coverage_boost.feature
  # Feature: System commands coverage boost
  # ============================================================

  Scenario: git sha returns unknown when git not available
    Given a system module for coverage testing
    When I call git_sha with git not on path
    Then the git sha should be "unknown"


  Scenario: dep_version returns not installed for missing package
    Given a system module for coverage testing
    When I call dep_version for a missing package
    Then the dep version should be "not installed"


  Scenario: build_info_data with missing database path
    Given a system module for coverage testing
    When I call build_info_data with no database file
    Then the info data should contain db_size as "0 MB"


  Scenario: build_info_data with missing log directory
    Given a system module for coverage testing
    When I call build_info_data with no log directory
    Then the info data should contain logs as "0 MB"


  Scenario: check_data_dir with non-existent dir
    Given a system module for coverage testing
    When I call check_data_dir with non-existent directory
    Then the check result should have WARN status


  Scenario: check_database with non-sqlite URL
    Given a system module for coverage testing
    When I call check_database with postgres URL
    Then the check result should have OK status and details "configured"


  Scenario: check_database with missing parent dir
    Given a system module for coverage testing
    When I call check_database with sqlite and non-writable parent
    Then the check result should have ERROR status


  Scenario: check_file_permissions with non-existent data dir
    Given a system module for coverage testing
    When I call check_file_permissions with non-existent directory
    Then the check result should have WARN status


  Scenario: check_file_permissions with read-only data dir
    Given a system module for coverage testing
    When I call check_file_permissions with read-only directory
    Then the file perm result status should not be OK


  Scenario: check_git when git is unavailable
    Given a system module for coverage testing
    When I call check_git with git unavailable
    Then the check result should have ERROR status


  Scenario: render_version_rich produces output
    Given a system module for coverage testing
    When I call render_version_rich with sample data
    Then no system exception should be raised


  Scenario: render_info_rich produces output
    Given a system module for coverage testing
    When I call render_info_rich with sample data
    Then no system exception should be raised


  Scenario: render_diagnostics_rich with errors
    Given a system module for coverage testing
    When I call render_diagnostics_rich with error data
    Then no system exception should be raised


  Scenario: render_diagnostics_rich with warnings only
    Given a system module for coverage testing
    When I call render_diagnostics_rich with warning data
    Then no system exception should be raised


  Scenario: render_diagnostics_rich with all OK
    Given a system module for coverage testing
    When I call render_diagnostics_rich with all OK data
    Then no system exception should be raised


  # ============================================================
  # Originally from: yaml_engine_direct_coverage.feature
  # Feature: YAML engine direct coverage
  # ============================================================

  Scenario: Engine construction registers filters and globals
    When I construct a new YAMLTemplateEngine for direct testing
    Then the direct engine should have yaml indent sum selectattr filters
    And the direct engine globals should include builtins


  Scenario: load_file reads and parses YAML from a temp file
    Given I construct a new YAMLTemplateEngine for direct testing
    And I write a temp YAML file with mapping content
    When I call load_file on the direct engine without context
    Then the direct load_file result should be a dict with app key


  Scenario: load_string parses plain YAML mapping
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call load_string with plain mapping YAML on direct engine
    Then the direct result should have key server


  Scenario: load_string raises for plain YAML list
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call load_string with plain list YAML on direct engine
    Then a direct ValueError mentioning dict should be raised


  Scenario: load_string with context renders and parses template
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call load_string with template and context on direct engine
    Then the direct rendered result should have substituted values


  Scenario: load_string without context defers rendering
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call load_string with template but no context on direct engine
    Then the direct deferred result should be a dict or raise YAMLError


  Scenario: preprocess inserts indent hints for for loops
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call preprocess on content with a for loop
    Then the direct preprocessed result should contain indent hint


  Scenario: postprocess removes blank lines before unindented content
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call postprocess on content with blank lines before unindented
    Then blank lines before unindented content should be gone


  Scenario: fix_common_yaml_issues splits multi-colon lines
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call fix_common_yaml_issues on multi-colon content
    Then the fixed content should have multiple lines


  Scenario: analyze_yaml_structure finds blocks and inline templates
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call analyze_yaml_structure on mixed content
    Then template_blocks and inline_templates should be populated
    And hierarchy should have root key


  Scenario: yaml_filter converts dict to YAML string
    When I call the static yaml_filter with a dict
    Then the yaml_filter result should contain key colon


  Scenario: indent_filter indents lines
    When I call the static indent_filter with 4 spaces
    Then each direct line should start with 4 spaces


  Scenario: sum_filter returns numeric sum
    When I call the static sum_filter on a number list
    Then the direct sum should be 10


  Scenario: selectattr_filter extracts with defaults
    When I call the static selectattr_filter for score
    Then items with score return their value and others return 0


  Scenario: create_render_context merges nested and top-level
    When I call the static create_render_context with nested data
    Then the direct context should have both nested and explicit keys


  Scenario: render_and_parse falls back on initial YAML error
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call render_and_parse with colon-heavy value
    Then the direct engine should attempt fix or raise


  Scenario: render_and_parse raises ValueError for non-dict result
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call render_and_parse that produces a list
    Then a direct ValueError mentioning dict should be raised


  Scenario: simple_template_extraction raises YAMLError for invalid
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call simple_template_extraction with invalid YAML
    Then a direct YAML error should be raised


  Scenario: simple_template_extraction raises ValueError for non-dict
    Given I construct a new YAMLTemplateEngine for direct testing
    When I call simple_template_extraction with list YAML
    Then a direct ValueError mentioning dict should be raised


  Scenario: load_file with context renders templates from disk
    Given I construct a new YAMLTemplateEngine for direct testing
    And I write a temp YAML file with template content
    When I call load_file with template context on direct engine
    Then the direct file result should contain rendered values

