Feature: v3 Actor YAML schema support in CLI and registry
  As a developer using v3 actor YAML configs
  I want the CLI and registry to validate, persist, and execute v3 schema actors
  So that spec-compliant actors with skills, LSP bindings, and LangGraph routes work

  Scenario: ActorConfiguration.from_blob extracts provider and model from v3 LLM YAML
    Given a v3 LLM actor blob with model "gpt-4"
    When I call ActorConfiguration.from_blob with the v3 blob
    Then the configuration should have provider "custom"
    And the configuration should have model "gpt-4"

  Scenario: ActorConfiguration.from_blob infers provider from model with slash
    Given a v3 LLM actor blob with model "openai/gpt-4"
    When I call ActorConfiguration.from_blob with the v3 blob
    Then the configuration should have provider "openai"
    And the configuration should have model "openai/gpt-4"

  Scenario: ActorConfiguration.from_blob infers custom provider for slash-prefixed model
    Given a v3 LLM actor blob with model "/gpt-4"
    When I call ActorConfiguration.from_blob with the v3 blob
    Then the configuration should have provider "custom"
    And the configuration should have model "/gpt-4"

  Scenario: ActorConfiguration.from_blob builds graph descriptor for graph actors
    Given a v3 graph actor blob with a route block
    When I call ActorConfiguration.from_blob with the v3 blob
    Then the configuration should have a graph_descriptor with type "graph"

  Scenario: ActorRegistry.add validates v3 LLM actor YAML
    Given a mock actor service for v3 testing
    And a v3 LLM actor YAML text
    When I call ActorRegistry.add with the v3 YAML
    Then the actor should be persisted with v3 fields in config_blob
    And the persisted config_blob should contain skills
    And the persisted config_blob should contain lsp bindings

  Scenario: ActorRegistry.add rejects v3 YAML without required description
    Given a mock actor service for v3 testing
    And a v3 actor YAML text missing description
    When I call ActorRegistry.add with the invalid v3 YAML
    Then a ValidationError should be raised mentioning description

  Scenario: ActorRegistry.add validates and compiles v3 graph actor
    Given a mock actor service for v3 testing
    And a v3 graph actor YAML text with route
    When I call ActorRegistry.add with the v3 graph YAML
    Then the actor should be persisted with compiled metadata
    And the graph_descriptor should contain route information

  Scenario: ReactiveConfigParser handles v3 LLM actor format
    Given a v3 LLM actor config dict
    When I parse the v3 config through ReactiveConfigParser
    Then the reactive config should have one agent
    And the agent config should contain the model
    And the agent config should contain skills

  Scenario: ReactiveConfigParser handles v3 graph actor format
    Given a v3 graph actor config dict with route
    When I parse the v3 config through ReactiveConfigParser
    Then the reactive config should have agents for each node
    And the reactive config should have a graph route
    And the graph route edges should use source and target keys

  # M14: test update=True path
  Scenario: ActorRegistry.add with update=True overwrites existing actor
    Given a mock actor service for v3 testing
    And an existing actor in the mock service
    And a v3 LLM actor YAML text
    When I call ActorRegistry.add with update=True for the v3 YAML
    Then the actor should be persisted successfully with update

  # M15: test type:tool extraction and parsing paths
  # from_blob requires model for ActorConfiguration — tool actors without
  # model raise ValueError.  The proper v3 path is ActorRegistry.add().
  Scenario: ActorConfiguration.from_blob rejects type tool without model
    Given a v3 tool actor blob without model
    When I call ActorConfiguration.from_blob with the v3 tool blob
    Then the tool actor from_blob should raise ValueError for missing model

  Scenario: ActorRegistry.add validates v3 tool actor YAML
    Given a mock actor service for v3 testing
    And a v3 tool actor YAML text
    When I call ActorRegistry.add with the v3 tool YAML
    Then the tool actor should be persisted with type tool

  # m13: test _build_from_v3 with lsp as dict
  Scenario: ReactiveConfigParser handles v3 LLM actor with lsp as dict
    Given a v3 LLM actor config dict with lsp as dict
    When I parse the v3 config through ReactiveConfigParser
    Then the reactive config should have one agent
    And the agent config should contain lsp as dict

  # m13: test context_view, memory, env_vars, response_format propagation
  Scenario: ReactiveConfigParser propagates context_view memory env_vars and response_format
    Given a v3 LLM actor config dict with context_view and memory
    When I parse the v3 config through ReactiveConfigParser
    Then the reactive config should have one agent
    And the agent config should contain context_view
    And the agent config should contain memory settings
    And the agent config should contain env_vars
    And the agent config should contain response_format

  # m13: positive _is_v3_format test
  Scenario: v3 format detection returns true for v3 LLM data
    Given a v3 LLM actor config dict
    When I check if the v3 LLM config is v3 format
    Then it should be detected as v3

  # Cycle 2 review: defensive guard BDD coverage
  Scenario: ReactiveConfigParser rejects graph with invalid entry_node
    Given a v3 graph actor config dict with invalid entry_node
    When I parse the v3 config through ReactiveConfigParser expecting error
    Then a ConfigurationError should be raised mentioning entry_node

  Scenario: ReactiveConfigParser skips edges with missing from_node or to_node
    Given a v3 graph actor config dict with incomplete edges
    When I parse the v3 config through ReactiveConfigParser
    Then the graph route should have fewer edges than the raw input

  Scenario: ReactiveConfigParser skips non-dict edge entries
    Given a v3 graph actor config dict with non-dict edges
    When I parse the v3 config through ReactiveConfigParser
    Then the graph route should have only valid dict edges

  # Coverage: v3_registry.py — name without slash gets local/ prefix
  Scenario: ActorRegistry.add namespaces bare actor name with local prefix
    Given a mock actor service for v3 testing
    And a v3 LLM actor YAML text with bare name
    When I call ActorRegistry.add with the bare-name v3 YAML
    Then the persisted actor name should be prefixed with local

  # Coverage: v3_registry.py — unsafe actor rejected without allow_unsafe
  Scenario: ActorRegistry.add rejects unsafe v3 actor without allow_unsafe flag
    Given a mock actor service for v3 testing
    And a v3 LLM actor YAML text marked unsafe
    When I call ActorRegistry.add with the unsafe v3 YAML
    Then a ValidationError should be raised mentioning unsafe

  # Coverage: v3_registry.py — duplicate actor rejected when update=False
  Scenario: ActorRegistry.add rejects duplicate v3 actor when update is False
    Given a mock actor service for v3 testing
    And an existing actor in the mock service
    And a v3 LLM actor YAML text
    When I call ActorRegistry.add with the v3 YAML expecting error
    Then a ValidationError should be raised mentioning already exists

  # Coverage: v3_registry.py — ActorCompilationError during graph compilation
  Scenario: ActorRegistry.add persists graph actor even when compilation fails
    Given a mock actor service for v3 testing
    And a v3 graph actor YAML text with route
    When I call ActorRegistry.add with the v3 graph YAML and compilation fails
    Then the actor should still be persisted without compiled metadata

  # Coverage: registry.py — _ensure_namespaced via get()
  Scenario: ActorRegistry.get namespaces bare actor name with local prefix
    Given a mock actor service for v3 testing
    When I call ActorRegistry.get with a bare actor name
    Then the actor service should be queried with local-prefixed name

  # Coverage: registry.py — add() with non-dict YAML
  Scenario: ActorRegistry.add rejects non-mapping YAML
    Given a mock actor service for v3 testing
    When I call ActorRegistry.add with a non-mapping YAML string
    Then a ValidationError should be raised mentioning mapping

  # Coverage: registry.py — upsert_actor v3 validation failure
  Scenario: ActorRegistry.upsert_actor rejects invalid v3 config_blob
    Given a mock actor service for v3 testing
    When I call ActorRegistry.upsert_actor with an invalid v3 config_blob
    Then a ValidationError should be raised mentioning invalid v3 actor

  # Coverage: config_parser.py — _merge with new=None
  Scenario: ReactiveConfigParser merge handles None new dict gracefully
    When I merge a None dict into a base config
    Then the base config should remain unchanged

  # Coverage: config_parser.py — missing model for llm actor
  Scenario: ReactiveConfigParser rejects v3 LLM actor with missing model
    Given a v3 LLM actor config dict with no model field
    When I parse the v3 config through ReactiveConfigParser expecting error
    Then a ConfigurationError should be raised mentioning model

  # Coverage: config_parser.py — lsp_capabilities and lsp_context_enrichment propagation
  Scenario: ReactiveConfigParser propagates lsp_capabilities and lsp_context_enrichment
    Given a v3 LLM actor config dict with lsp_capabilities and lsp_context_enrichment
    When I parse the v3 config through ReactiveConfigParser
    Then the agent config should contain lsp_capabilities
    And the agent config should contain lsp_context_enrichment

  # Coverage: config_parser.py — graph actor without route raises ConfigurationError
  Scenario: ReactiveConfigParser rejects v3 graph actor without route
    Given a v3 graph actor config dict without route
    When I parse the v3 config through ReactiveConfigParser expecting error
    Then a ConfigurationError should be raised mentioning route

  # Coverage: config_parser.py — non-dict node and empty-id node skipped in graph
  Scenario: ReactiveConfigParser skips non-dict and empty-id nodes in graph route
    Given a v3 graph actor config dict with malformed nodes
    When I parse the v3 config through ReactiveConfigParser
    Then the reactive config should only have agents for valid nodes

  # Coverage: config_parser.py — skills and lsp propagated to graph nodes
  Scenario: ReactiveConfigParser propagates skills and lsp bindings to graph nodes
    Given a v3 graph actor config dict with skills and lsp
    When I parse the v3 config through ReactiveConfigParser
    Then each graph node agent should have skills propagated
    And each graph node agent should have lsp propagated

  # Coverage: config_parser.py — lsp as dict propagated to graph nodes (line 330)
  Scenario: ReactiveConfigParser propagates lsp dict bindings to graph nodes
    Given a v3 graph actor config dict with skills and lsp as dict
    When I parse the v3 config through ReactiveConfigParser
    Then each graph node agent should have lsp dict propagated

  # M5: options block forwarded to agent_config in _build_from_v3 (#11223).
  @tdd_issue @tdd_issue_11223
  Scenario: ReactiveConfigParser propagates options block to agent config
    Given a v3 LLM actor config dict with options block
    When I parse the v3 config through ReactiveConfigParser
    Then the agent config should contain the options block

  # M5: actor without options block is unaffected (#11223).
  @tdd_issue @tdd_issue_11223
  Scenario: ReactiveConfigParser handles v3 actor without options block
    Given a v3 LLM actor config dict without options block
    When I parse the v3 config through ReactiveConfigParser
    Then the agent config should not contain an options key

  # M5: empty options dict is preserved (#11223).
  @tdd_issue @tdd_issue_11223
  Scenario: ReactiveConfigParser preserves empty options block
    Given a v3 LLM actor config dict with empty options block
    When I parse the v3 config through ReactiveConfigParser
    Then the agent config should contain an empty options block
