Feature: ACMS Backend Abstraction Layer
  As a developer
  I want text, vector, and graph backend protocols with in-memory stubs
  So that context strategies can query heterogeneous data stores uniformly

  # ---- TextResult ----

  Scenario: Create a valid TextResult
    Given a TextResult with uko_uri "uko-py:class/Auth" and score 0.9
    Then the text result uko_uri should be "uko-py:class/Auth"
    And the text result content should be "class Auth: pass"
    And the text result score should be 0.9
    And the text result metadata should be empty

  Scenario: TextResult rejects empty uko_uri
    Then creating a TextResult with empty uko_uri should raise ValueError

  Scenario: TextResult rejects score above 1.0
    Then creating a TextResult with score 1.5 should raise ValueError

  Scenario: TextResult rejects score below 0.0
    Then creating a TextResult with score -0.1 should raise ValueError

  Scenario: TextResult is immutable
    Given a TextResult with uko_uri "uko-py:func/login" and score 0.5
    Then modifying the text result score should raise an error

  # ---- VectorResult ----

  Scenario: Create a valid VectorResult
    Given a VectorResult with uko_uri "uko-py:class/VecStore" and score 0.85
    Then the vector result uko_uri should be "uko-py:class/VecStore"
    And the vector result content should be "vector store content"
    And the vector result score should be 0.85
    And the vector result metadata should be empty

  Scenario: VectorResult rejects empty uko_uri
    Then creating a VectorResult with empty uko_uri should raise ValueError

  Scenario: VectorResult rejects score above 1.0
    Then creating a VectorResult with score 1.5 should raise ValueError

  Scenario: VectorResult rejects score below 0.0
    Then creating a VectorResult with score -0.1 should raise ValueError

  Scenario: VectorResult is immutable
    Given a VectorResult with uko_uri "uko-py:func/embed" and score 0.7
    Then modifying the vector result score should raise an error

  # ---- GraphResult ----

  Scenario: Create a valid GraphResult with no triples
    Given a GraphResult with no triples
    Then the graph result triples should be empty
    And the graph result metadata should be empty

  Scenario: Create a GraphResult with triples
    Given a GraphResult with triple "uko:A" "uko:contains" "uko:B"
    Then the graph result should have 1 triple

  Scenario: GraphResult is immutable
    Given a GraphResult with no triples
    Then modifying the graph result metadata should raise an error

  # ---- InMemoryTextBackend ----

  Scenario: InMemoryTextBackend satisfies TextBackend protocol
    Given an InMemoryTextBackend instance
    Then it should be a TextBackend

  Scenario: InMemoryTextBackend search returns empty list
    Given an InMemoryTextBackend instance
    When I search for "auth flow" with scope "RES01"
    Then the text search results should be empty

  Scenario: InMemoryTextBackend search with max_results
    Given an InMemoryTextBackend instance
    When I search for "auth" with scope "RES01" and max_results 5
    Then the text search results should be empty

  Scenario: InMemoryTextBackend rejects empty query
    Given an InMemoryTextBackend instance
    Then searching with empty query should raise ValueError

  Scenario: InMemoryTextBackend rejects non-positive max_results
    Given an InMemoryTextBackend instance
    Then searching with max_results 0 should raise ValueError

  # ---- InMemoryVectorBackend ----

  Scenario: InMemoryVectorBackend satisfies VectorBackend protocol
    Given an InMemoryVectorBackend instance
    Then it should be a VectorBackend

  Scenario: InMemoryVectorBackend search returns empty list
    Given an InMemoryVectorBackend instance
    When I similarity search with embedding and scope "RES01"
    Then the vector search results should be empty

  Scenario: InMemoryVectorBackend search with top_k
    Given an InMemoryVectorBackend instance
    When I similarity search with embedding and scope "RES01" and top_k 5
    Then the vector search results should be empty

  Scenario: InMemoryVectorBackend rejects empty embedding
    Given an InMemoryVectorBackend instance
    Then similarity searching with empty embedding should raise ValueError

  Scenario: InMemoryVectorBackend rejects non-positive top_k
    Given an InMemoryVectorBackend instance
    Then similarity searching with top_k 0 should raise ValueError

  # ---- InMemoryGraphBackend ----

  Scenario: InMemoryGraphBackend satisfies GraphBackend protocol
    Given an InMemoryGraphBackend instance
    Then it should be a GraphBackend

  Scenario: InMemoryGraphBackend sparql_query returns empty result
    Given an InMemoryGraphBackend instance
    When I run sparql query "SELECT ?s WHERE { ?s a uko:Container }" with scope "RES01"
    Then the graph query result should have no triples

  Scenario: InMemoryGraphBackend sparql_query rejects empty query
    Given an InMemoryGraphBackend instance
    Then sparql querying with empty query should raise ValueError

  Scenario: InMemoryGraphBackend get_triples returns empty result
    Given an InMemoryGraphBackend instance
    When I get triples for subject "uko-py:class/Auth"
    Then the graph query result should have no triples

  Scenario: InMemoryGraphBackend get_triples rejects empty subject
    Given an InMemoryGraphBackend instance
    Then getting triples with empty subject should raise ValueError

  Scenario: InMemoryGraphBackend traverse returns empty result
    Given an InMemoryGraphBackend instance
    When I traverse from "uko-py:class/Auth" with depth 3
    Then the graph query result should have no triples

  Scenario: InMemoryGraphBackend traverse rejects empty start
    Given an InMemoryGraphBackend instance
    Then traversing with empty start should raise ValueError

  Scenario: InMemoryGraphBackend traverse rejects negative depth
    Given an InMemoryGraphBackend instance
    Then traversing with depth -1 should raise ValueError

  # ---- DI Container Registration ----

  Scenario: DI container provides a TextBackend
    Given the DI container
    Then the container should provide a text_backend
    And the text_backend should be a TextBackend

  Scenario: DI container provides a VectorBackend
    Given the DI container
    Then the container should provide a vector_backend
    And the vector_backend should be a VectorBackend

  Scenario: DI container provides a GraphBackend
    Given the DI container
    Then the container should provide a graph_backend
    And the graph_backend should be a GraphBackend

  Scenario: DI container backends are singletons
    Given the DI container
    Then resolving text_backend twice should return the same instance
    And resolving vector_backend twice should return the same instance
    And resolving graph_backend twice should return the same instance
