Feature: Consolidated Actor Combined scenarios from: actor_compiler, actor_compiler_coverage, actor_config_new_coverage, actor_hierarchy, actor_registry_coverage, actor_registry_full_coverage, actor_registry_persistence, actor_runtime, m2_actor_tool_smoke # ============================================================ # Originally from: actor_compiler.feature # Feature: Actor compiler translates hierarchical actor configs to LangGraph # ============================================================ Scenario: Compile a simple two-node graph actor Given a GRAPH actor config with a linear two-node topology When I compile the actor config Then the compilation should succeed And the compiled actor should have 2 nodes And the compiled actor entry point should be "planner" And the compilation metadata should list node IDs "executor" and "planner" Scenario: Compile a graph with a tool node Given a GRAPH actor config with an agent and a tool node When I compile the actor config Then the compilation should succeed And the compilation metadata should have 1 tool node Scenario: Compile a graph with LSP bindings on a node Given a GRAPH actor config with LSP bindings on the agent node When I compile the actor config Then the compilation should succeed And the compilation metadata should have 1 LSP binding And the LSP binding should reference server "local/pyright" Scenario: Compile a graph with a conditional node Given a GRAPH actor config with a conditional routing node When I compile the actor config Then the compilation should succeed And the compiled actor should have 3 nodes Scenario: Compile a graph with a subgraph reference Given a GRAPH actor config with a subgraph node referencing "workflows/inner" When I compile the actor config with a resolver Then the compilation should succeed And the compilation metadata subgraph refs should map "sub" to "workflows/inner" Scenario: Compile a single-node graph actor Given a GRAPH actor config with a single node When I compile the actor config Then the compilation should succeed And the compiled actor should have 1 nodes # ──────────────────────────────────────────────────────────── # Subgraph resolution and cycle detection # ──────────────────────────────────────────────────────────── Scenario: Detect cross-actor subgraph cycle Given a GRAPH actor config "workflows/outer" referencing "workflows/inner" And a resolver where "workflows/inner" references "workflows/outer" When I compile the actor config with a resolver Then the compilation should fail with SubgraphCycleError And the compiler error message should contain "cycle" Scenario: Accept acyclic subgraph chain Given a GRAPH actor config "workflows/top" referencing "workflows/mid" And a resolver where "workflows/mid" has no subgraph nodes When I compile the actor config with a resolver Then the compilation should succeed # ──────────────────────────────────────────────────────────── # Missing node and invalid entry/exit errors # ──────────────────────────────────────────────────────────── Scenario: Reject non-GRAPH actor type Given an LLM actor config When I attempt to compile the non-graph actor Then the compilation should fail with ActorCompilationError And the compiler error message should contain "GRAPH" Scenario: Reject GRAPH actor without route Given a GRAPH actor config with route set to None When I attempt to compile the routeless actor Then the compilation should fail with ActorCompilationError And the compiler error message should contain "no route" # ──────────────────────────────────────────────────────────── # Metadata inspection # ──────────────────────────────────────────────────────────── Scenario: Metadata lists all node IDs sorted Given a GRAPH actor config with nodes "alpha", "beta", and "gamma" When I compile the actor config Then the compilation should succeed And the metadata node IDs should be sorted alphabetically Scenario: Metadata records entry and exit nodes Given a GRAPH actor config with entry "start_node" and exit "end_node" When I compile the actor config Then the compilation should succeed And the metadata entry node should be "start_node" And the metadata exit nodes should contain "end_node" Scenario: Compilation metadata is JSON-serializable Given a GRAPH actor config with LSP bindings on the agent node When I compile the actor config Then the compilation metadata should be JSON-serializable # ============================================================ # Originally from: actor_compiler_coverage.feature # Feature: Actor compiler full coverage for node mapping, edge mapping, LSP bindings, and error paths # ============================================================ Scenario: Compile a graph with all four node types mapped correctly Given a GRAPH actor config containing AGENT, TOOL, CONDITIONAL, and SUBGRAPH nodes When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled node "agent_node" should have LangGraph type "agent" And the compiled node "tool_node" should have LangGraph type "tool" And the compiled node "cond_node" should have LangGraph type "conditional" And the compiled node "sub_node" should have LangGraph type "subgraph" Scenario: Agent node config maps agent field to NodeConfig Given a GRAPH actor config with an agent node configured as "my_agent" When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled node "agent_node" should have agent set to "my_agent" Scenario: Conditional node config maps function field to NodeConfig Given a GRAPH actor config with a conditional node configured with function "route_fn" When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled node "cond_node" should have function set to "route_fn" Scenario: Tool node config maps tools list to NodeConfig Given a GRAPH actor config with a tool node configured with tools "lint,format" When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled node "tool_node" should have tools "lint" and "format" Scenario: Subgraph node config maps actor_ref to NodeConfig subgraph field Given a GRAPH actor config with a subgraph node referencing actor "workflows/child" When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled node "sub_node" should have subgraph set to "workflows/child" # ──────────────────────────────────────────────────────────── # Edge mapping (_map_edge) # ──────────────────────────────────────────────────────────── Scenario: Edge without condition compiles to null condition field Given a GRAPH actor config with an unconditional edge from "a" to "b" When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled edge from "a" to "b" should have a null condition Scenario: Edge with condition compiles to expression dict Given a GRAPH actor config with a conditional edge from "a" to "b" with condition "state.get('done')" When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled edge from "a" to "b" should have condition expression "state.get('done')" Scenario: Edge priority is recorded in compiled edge metadata Given a GRAPH actor config with an edge from "a" to "b" with priority 5 When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled edge from "a" to "b" should have metadata priority 5 # ──────────────────────────────────────────────────────────── # LSP binding extraction edge cases (_extract_lsp_bindings) # ──────────────────────────────────────────────────────────── Scenario: Non-list LSP bindings config value is safely ignored Given a GRAPH actor config where lsp_bindings is a string instead of a list When I compile the full-coverage actor config Then the compilation should succeed without errors And the compilation metadata should have 0 LSP bindings Scenario: Non-dict entry in LSP bindings list is skipped Given a GRAPH actor config where lsp_bindings contains a non-dict entry When I compile the full-coverage actor config Then the compilation should succeed without errors And the compilation metadata should have 0 LSP bindings Scenario: LSP binding with empty server name is skipped Given a GRAPH actor config where an LSP binding has an empty server name When I compile the full-coverage actor config Then the compilation should succeed without errors And the compilation metadata should have 0 LSP bindings Scenario: LSP binding with non-string server name is skipped Given a GRAPH actor config where an LSP binding has a numeric server name When I compile the full-coverage actor config Then the compilation should succeed without errors And the compilation metadata should have 0 LSP bindings Scenario: LSP binding with missing server name key is skipped Given a GRAPH actor config where an LSP binding is missing the server name key When I compile the full-coverage actor config Then the compilation should succeed without errors And the compilation metadata should have 0 LSP bindings Scenario: Multiple valid LSP bindings are extracted from a single node Given a GRAPH actor config with two valid LSP bindings on one node When I compile the full-coverage actor config Then the compilation should succeed without errors And the compilation metadata should have 2 LSP bindings And the LSP binding servers should include "local/pyright" and "local/ts" # ──────────────────────────────────────────────────────────── # Subgraph cycle detection edge cases (_detect_subgraph_cycles) # ──────────────────────────────────────────────────────────── Scenario: Subgraph node with empty actor_ref is skipped during cycle detection Given a GRAPH actor config with a subgraph node that has an empty actor_ref When I compile the full-coverage actor config with a resolver Then the compilation should succeed without errors Scenario: Resolver returning None for a referenced actor skips that node Given a GRAPH actor config with a subgraph referencing actor name "workflows/missing" And a cycle-detection resolver that returns None for all names When I compile the full-coverage actor config with a resolver Then the compilation should succeed without errors Scenario: Referenced actor that is not GRAPH type is skipped during cycle detection Given a GRAPH actor config with a subgraph referencing actor name "tools/helper" And a cycle-detection resolver where "tools/helper" is an LLM type actor When I compile the full-coverage actor config with a resolver Then the compilation should succeed without errors Scenario: Referenced actor with no route definition is skipped during cycle detection Given a GRAPH actor config with a subgraph referencing actor name "workflows/no-route" And a cycle-detection resolver where "workflows/no-route" is a GRAPH actor with no route When I compile the full-coverage actor config with a resolver Then the compilation should succeed without errors Scenario: Deep three-level subgraph cycle is detected Given a GRAPH actor config named "workflows/alpha" with subgraph ref to "workflows/beta" And a cycle-detection resolver where "workflows/beta" references "workflows/gamma" and "workflows/gamma" references "workflows/alpha" When I compile the full-coverage actor config with a resolver Then the compilation should fail with a SubgraphCycleError And the compilation error message should mention "cycle" # ──────────────────────────────────────────────────────────── # compile_actor error paths # ──────────────────────────────────────────────────────────── Scenario: Intra-graph cycle raises SubgraphCycleError during compilation Given a GRAPH actor config with a cyclic route that bypasses schema validation When I compile the full-coverage actor config Then the compilation should fail with a SubgraphCycleError And the compilation error message should mention "intra-graph" Scenario: Edge referencing missing from_node raises MissingNodeError Given a GRAPH actor config with an edge whose from_node does not exist in the node list When I compile the full-coverage actor config Then the compilation should fail with a MissingNodeError And the compilation error message should mention "from_node" Scenario: Edge referencing missing to_node raises MissingNodeError Given a GRAPH actor config with an edge whose to_node does not exist in the node list When I compile the full-coverage actor config Then the compilation should fail with a MissingNodeError And the compilation error message should mention "to_node" Scenario: Invalid entry node raises InvalidEntryExitError Given a GRAPH actor config whose entry node does not match any compiled node When I compile the full-coverage actor config Then the compilation should fail with an InvalidEntryExitError And the compilation error message should mention "Entry node" Scenario: Invalid exit node raises InvalidEntryExitError Given a GRAPH actor config whose exit node does not match any compiled node When I compile the full-coverage actor config Then the compilation should fail with an InvalidEntryExitError And the compilation error message should mention "Exit node" # ──────────────────────────────────────────────────────────── # Model defaults and result bundle # ──────────────────────────────────────────────────────────── Scenario: CompilationMetadata defaults to empty collections Given a freshly constructed CompilationMetadata with no arguments Then the metadata node_ids should be an empty list And the metadata tool_nodes should be an empty list And the metadata lsp_bindings should be an empty list And the metadata subgraph_refs should be an empty dict And the metadata entry_node should be an empty string And the metadata exit_nodes should be an empty list Scenario: CompiledActor preserves actor name and entry point from config Given a GRAPH actor config named "workflows/test" with entry "start" When I compile the full-coverage actor config Then the compilation should succeed without errors And the compiled actor name should be "workflows/test" And the compiled actor entry_point field should be "start" # ============================================================ # Originally from: actor_config_new_coverage.feature # Feature: Actor configuration new coverage # ============================================================ Scenario: load_blob_from_file loads JSON config Given a temporary JSON config file with provider and model When I load the blob from the JSON file Then the blob should contain provider and model keys Scenario: load_blob_from_file raises for missing file When I load the blob from a nonexistent path Then an actor config ValueError should mention "not found" Scenario: load_blob_from_file raises for non-dict JSON Given a temporary JSON config file with a list When I load the blob from the list JSON file Then an actor config ValueError should mention "object" Scenario: load_blob_from_file returns empty dict for null JSON Given a temporary JSON config file with null content When I load the blob from the null JSON file Then the blob should be an empty dict Scenario: load_blob_from_file falls back to YAML for invalid JSON Given a temporary YAML-only config file with provider and model When I load the blob from the YAML file Then the blob should contain provider and model keys Scenario: _load_v2_yaml_content parses plain YAML When I load v2 YAML content without templates Then the v2 result should be a dict with expected keys Scenario: _load_v2_yaml_content handles empty YAML When I load v2 YAML content that is empty Then the v2 result should be an empty dict Scenario: _load_v2_yaml_content rejects non-dict YAML When I load v2 YAML content that is a list Then an actor config ValueError should mention "object" Scenario: _load_v2_yaml_content processes templated YAML When I load v2 YAML content with Jinja2 templates Then the actor config result should be a dict Scenario: _restore_template_syntax restores markers in system_prompt When I restore template syntax in a config with protected markers Then the system_prompt should have Jinja2 syntax restored Scenario: _restore_template_syntax recurses into lists When I restore template syntax in a list config Then nested list items should have markers restored Scenario: _interpolate_env_vars substitutes environment variables Given environment variable "TEST_ACTOR_VAR" is set to "openai" When I interpolate env vars in config with "${TEST_ACTOR_VAR}" Then the interpolated value should be "openai" Scenario: _interpolate_env_vars uses default when env var missing When I interpolate env vars with "${MISSING_VAR:fallback}" Then the interpolated value should be "fallback" Scenario: _interpolate_env_vars raises when env var missing without default When I interpolate env vars with "${TOTALLY_MISSING_VAR}" and no default Then an actor config ValueError should mention "not set" Scenario: _interpolate_env_vars converts boolean strings When I interpolate env vars for string "true" Then the interpolated result should be boolean True Scenario: _interpolate_env_vars converts integer strings When I interpolate env vars for string "42" Then the interpolated result should be integer 42 Scenario: _interpolate_env_vars converts float strings When I interpolate env vars for string "3.14" Then the interpolated result should be float 3.14 Scenario: _interpolate_env_vars handles boolean defaults When I interpolate env vars with "${MISSING_BOOL:true}" Then the interpolated result should be string "True" Scenario: _interpolate_env_vars handles integer defaults When I interpolate env vars with "${MISSING_INT:99}" Then the interpolated result should be string "99" Scenario: _interpolate_env_vars recurses into dicts and lists When I interpolate env vars in a nested structure Then all nested string values should be interpolated Scenario: from_blob extracts provider and model When I create an ActorConfiguration from a blob with provider and model Then the config provider should be set And the config model should be set Scenario: from_blob raises when provider missing When I create an ActorConfiguration from a blob without provider Then an actor config ValueError should mention "provider" Scenario: from_blob raises when model missing When I create an ActorConfiguration from a blob without model Then an actor config ValueError should mention "model" Scenario: from_blob merges option overrides When I create an ActorConfiguration from a blob with option overrides Then the config options should include the overrides Scenario: from_file loads and parses config from disk Given a temporary JSON config file with provider and model When I create an ActorConfiguration from the file Then the config provider should be set And the config model should be set # ============================================================ # Originally from: actor_hierarchy.feature # Feature: Hierarchical Actor YAML Schema Extensions # ============================================================ Scenario: Node with explicit LSP binding Given a hierarchy actor YAML with node lsp_binding When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy graph node "code_writer" should have lsp_binding And the hierarchy node "code_writer" lsp_binding server should be "local/pyright" And the hierarchy node "code_writer" lsp_binding languages should contain "python" Scenario: Node with auto LSP binding Given a hierarchy actor YAML with auto lsp_binding When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy graph node "implementer" should have lsp_binding And the hierarchy node "implementer" lsp_binding auto should be true Scenario: Node without LSP binding is valid Given a hierarchy actor YAML with no lsp_binding When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy graph node "agent_node" should not have lsp_binding Scenario: LSP binding with invalid server name Given a hierarchy actor YAML with lsp_binding server "no-namespace" When I parse the hierarchy actor YAML expecting failure Then the hierarchy parse error should mention "lsp_binding" # ------------------------------------------------------------------- # Tool-Source References on NodeDefinition # ------------------------------------------------------------------- Scenario: Node with tool_sources referencing skills Given a hierarchy actor YAML with node tool_sources skills When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy graph node "executor" should have tool_sources And the hierarchy node "executor" tool_sources should have 1 items Scenario: Node with tool_sources referencing MCP server Given a hierarchy actor YAML with node tool_sources mcp When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy node "executor" tool_sources item 0 type should be "mcp" And the hierarchy node "executor" tool_sources item 0 name should be "local/filesystem" Scenario: Node with tool_sources referencing builtin group Given a hierarchy actor YAML with node tool_sources builtin When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy node "executor" tool_sources item 0 type should be "builtin" And the hierarchy node "executor" tool_sources item 0 group should be "file_operations" Scenario: Node with mixed tool_sources Given a hierarchy actor YAML with mixed node tool_sources When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy node "executor" tool_sources should have 3 items Scenario: Node with invalid tool_sources type Given a hierarchy actor YAML with invalid tool_sources type When I parse the hierarchy actor YAML expecting failure Then the hierarchy parse error should mention "tool_sources" Scenario: Node without tool_sources is valid Given a hierarchy actor YAML with no lsp_binding When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy graph node "agent_node" should not have tool_sources # ------------------------------------------------------------------- # Subgraph Node Enhancements # ------------------------------------------------------------------- Scenario: Subgraph node with actor_ref Given a hierarchy actor YAML with subgraph actor_ref When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy graph node "review" should have type "subgraph" And the hierarchy node "review" actor_ref should be "local/code-reviewer" Scenario: Subgraph node with actor_ref non-namespaced fails Given a hierarchy actor YAML with subgraph actor_ref "bad-ref" When I parse the hierarchy actor YAML expecting failure Then the hierarchy parse error should mention "actor_ref" # ------------------------------------------------------------------- # Skills Field on ActorConfigSchema # ------------------------------------------------------------------- Scenario: Actor with skills list Given a hierarchy actor YAML with skills list When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy actor should have 2 skills Scenario: Actor with empty skills is valid Given a hierarchy actor YAML with empty skills When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy actor should have 0 skills # ------------------------------------------------------------------- # LSP Fields on ActorConfigSchema (top-level) # ------------------------------------------------------------------- Scenario: Actor with lsp list Given a hierarchy actor YAML with lsp list When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy actor lsp should have 2 items Scenario: Actor with lsp auto binding Given a hierarchy actor YAML with lsp auto When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy actor lsp_auto should be true Scenario: Actor with lsp_capabilities list Given a hierarchy actor YAML with lsp_capabilities When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy actor lsp_capabilities should contain "diagnostics" Scenario: Actor with lsp_capabilities all Given a hierarchy actor YAML with lsp_capabilities all When I parse the hierarchy actor YAML Then the hierarchy actor should be valid And the hierarchy actor lsp_capabilities should be "all" # ------------------------------------------------------------------- # Precise Validation Error Messages # ------------------------------------------------------------------- Scenario: Missing entry node gives precise field path Given a hierarchy actor YAML with bad entry_node "nonexistent" When I parse the hierarchy actor YAML expecting failure Then the hierarchy parse error should mention "entry_node" And the hierarchy parse error should mention "nonexistent" Scenario: Duplicate node IDs gives precise error Given a hierarchy actor YAML with duplicate node ids When I parse the hierarchy actor YAML expecting failure Then the hierarchy parse error should mention "Duplicate" Scenario: Cycle detection gives node path Given a hierarchy actor YAML with cycle When I parse the hierarchy actor YAML expecting failure Then the hierarchy parse error should mention "cycle" Scenario: Edge referencing non-existent node gives precise error Given a hierarchy actor YAML with bad edge from_node "ghost" When I parse the hierarchy actor YAML expecting failure Then the hierarchy parse error should mention "ghost" # ------------------------------------------------------------------- # Loader Integration with Hierarchical Actors # ------------------------------------------------------------------- Scenario: Loader discovers hierarchical actor YAML Given a hierarchy temp dir with a hierarchical actor YAML file When I create a hierarchy loader and run discovery Then the hierarchy loader should find 1 actors And the hierarchy loaded actor should have skills And the hierarchy loaded actor should have lsp Scenario: Loader rejects actor with invalid lsp_binding Given a hierarchy temp dir with invalid lsp_binding actor YAML When I create a hierarchy loader and run discovery expecting error Then the hierarchy loader error should mention "lsp_binding" # ============================================================ # Originally from: actor_registry_coverage.feature # Feature: Actor registry coverage # ============================================================ # ============================================================ # Originally from: actor_registry_full_coverage.feature # Feature: Actor registry full coverage # ============================================================ Scenario: Registry returns empty list when no providers are configured Given a fresh actor registry with no providers When I run ensure_built_in_actors Then the generated actor list should be empty Scenario: Registry resolves virtual built-in actors from configured providers in alphabetical order Given a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | | anthropic | Claude | opus | When I run ensure_built_in_actors Then the generated actors should be ["claude/opus", "openai/gpt-4o-mini"] Scenario: Registry resolves virtual built-in actors including preferred provider Given preferred provider defaults provider "claude" and model "opus" And a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | | anthropic | Claude | opus | When I run ensure_built_in_actors Then the generated actors should be ["claude/opus", "openai/gpt-4o-mini"] Scenario: Registry virtual resolution does not overwrite pre-existing default preference Given preferred provider defaults provider "claude" and model "opus" And a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | | anthropic | Claude | opus | And a pre-existing default actor "OpenAI/gpt-4o-mini" When I run ensure_built_in_actors Then the service default actor should be "OpenAI/gpt-4o-mini" Scenario: Built-in actor includes graph descriptor with provider registry source and capabilities Given a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | When I run ensure_built_in_actors Then the built-in actor "openai/gpt-4o-mini" should have graph descriptor with source "provider-registry" And the built-in actor "openai/gpt-4o-mini" config blob should include capabilities # ── upsert_actor ──────────────────────────────────────────────────── Scenario: Registry upserts a custom actor with provider model and graph descriptor Given a fresh actor registry with no providers When I upsert a custom actor "custom/demo" with provider "custom" model "demo" and graph {"flow": "simple"} Then the upserted actor should have name "custom/demo" And the upserted actor should have provider "custom" and model "demo" Scenario: Registry rejects unsafe actor configuration without explicit unsafe flag Given a fresh actor registry with no providers When I attempt to upsert an unsafe actor "local/danger" without allow_unsafe Then a ValidationError should be raised with message containing "unsafe" Scenario: Registry allows unsafe actor when allow_unsafe flag is set Given a fresh actor registry with no providers When I upsert an unsafe actor "local/allowed" with allow_unsafe flag Then the upserted actor should have name "local/allowed" And the upserted actor should be marked unsafe Scenario: Registry allows unsafe actor when allow_unsafe flag is set (is_built_in removed) Given a fresh actor registry with no providers When I upsert an unsafe actor "local/builtin" with allow_unsafe flag Then the upserted actor should have name "local/builtin" And the upserted actor should be marked unsafe Scenario: Registry builds default graph descriptor when provider and model are supplied Given a fresh actor registry with no providers When I upsert a custom actor "local/graphed" with provider "openai" model "gpt-4o" and no explicit graph Then the upserted actor config blob should contain a graph_descriptor with provider "openai" and model "gpt-4o" Scenario: Registry upsert canonicalizes blob with default options and source Given a fresh actor registry with no providers When I upsert actor "local/canon" with config blob {"provider": "test", "model": "m1", "foo": "bar"} Then the upserted actor config blob should have key "source" with value "custom" And the upserted actor config blob should have key "unsafe" with value false # ── Wrapper methods ───────────────────────────────────────────────── Scenario: Registry get_actor delegates to actor service after ensuring built-ins Given a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | When I call registry get_actor with "openai/gpt-4o-mini" Then the fetched actor via registry should be named "openai/gpt-4o-mini" Scenario: Registry list_actors returns virtual built-in actors without DB writes Given a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | | anthropic | Claude | opus | When I call registry list_actors Then the returned actor list should contain ["claude/opus", "openai/gpt-4o-mini"] Scenario: Registry remove_actor rejects virtual built-in actors with ValidationError Given a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | | anthropic | Claude | opus | When I call registry remove_actor with "openai/gpt-4o-mini" Then the remove_actor call should have raised a ValidationError Scenario: Registry remove_actor delegates to actor service for custom actors Given a fresh actor registry with no providers When I call registry remove_actor with "local/my-custom-actor" Then the service should no longer contain actor "local/my-custom-actor" Scenario: Registry set_default_actor stores virtual built-in name as preference Given a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | | anthropic | Claude | opus | When I call registry set_default_actor with "claude/opus" And I call registry get_default_actor Then the returned default actor should be "claude/opus" Scenario: Registry get_default_actor returns None when no default is configured Given a fresh actor registry with providers | type | name | model | | openai | OpenAI | gpt-4o-mini | When I call registry get_default_actor Then the returned default actor should be None # ── _canonical_blob ───────────────────────────────────────────────── Scenario: Canonical blob sets provider model and source defaults from configuration Given a fresh actor registry with no providers When I upsert actor "local/defaults" with config blob {"provider": "p1", "model": "m1"} Then the upserted actor config blob should have key "provider" with value "p1" And the upserted actor config blob should have key "model" with value "m1" And the upserted actor config blob should have key "source" with value "custom" Scenario: Canonical blob preserves existing graph descriptor in raw blob Given a fresh actor registry with no providers When I upsert actor "local/existing" with config blob {"provider": "p1", "model": "m1", "graph_descriptor": {"custom": true}} Then the upserted actor config blob graph_descriptor should equal {"custom": true} Scenario: Canonical blob handles non-dict options by resetting to empty dict Given a fresh actor registry with no providers When I upsert actor "local/badopts" with config blob {"provider": "p1", "model": "m1", "options": "not-a-dict"} Then the upserted actor config blob should have key "options" with value {} # ── _build_graph_descriptor ───────────────────────────────────────── Scenario: Build graph descriptor includes provider model source and capabilities Given a fresh actor registry with no providers When I call _build_graph_descriptor with provider "openai" model "gpt-4o" source "test" and capabilities Then the graph descriptor should have provider "openai" model "gpt-4o" source "test" And the graph descriptor capabilities should not be None Scenario: Build graph descriptor omits capabilities when not provided Given a fresh actor registry with no providers When I call _build_graph_descriptor with provider "openai" model "gpt-4o" source "test" and no capabilities Then the graph descriptor should have provider "openai" model "gpt-4o" source "test" And the graph descriptor capabilities should be None # ============================================================ # Originally from: actor_registry_persistence.feature # Feature: Actor registry persistence with YAML text retention # ============================================================ Scenario: Add actor from YAML text retains original source Given an actor registry with no configured providers for persistence When I add actor from YAML text: """ name: local/test-actor type: llm description: A test actor provider: openai model: gpt-4 """ Then the actor "local/test-actor" should have yaml_text containing "provider: openai" And the actor "local/test-actor" should have schema_version "1.0" Scenario: Add actor with explicit schema version Given an actor registry with no configured providers for persistence When I add actor from YAML text with schema_version "2.0": """ name: local/versioned type: llm description: A versioned actor provider: anthropic model: claude-3 """ Then the actor "local/versioned" should have schema_version "2.0" Scenario: Add actor with compiled metadata Given an actor registry with no configured providers for persistence When I add actor from YAML text with compiled_metadata: """ name: local/compiled type: llm description: A compiled actor provider: openai model: gpt-4o """ Then the actor "local/compiled" should have compiled_metadata key "graph_nodes" Scenario: Update existing actor preserves YAML text Given an actor registry with no configured providers for persistence When I add actor from YAML text: """ name: local/updatable type: llm description: An updatable actor provider: openai model: gpt-4 """ And I update actor from YAML text: """ name: local/updatable type: llm description: An updatable actor provider: openai model: gpt-4-turbo """ Then the actor "local/updatable" should have yaml_text containing "gpt-4-turbo" Scenario: Remove actor by namespaced name Given an actor registry with no configured providers for persistence When I add actor from YAML text: """ name: local/removable type: llm description: A removable actor provider: openai model: gpt-4 """ And I remove actor "local/removable" via registry Then the actor "local/removable" should not exist Scenario: List actors with namespace filter Given an actor registry with no configured providers for persistence When I add actor from YAML text: """ name: local/alpha type: llm description: An alpha actor provider: openai model: gpt-4 """ And I add actor from YAML text with update: """ name: local/beta type: llm description: A beta actor provider: anthropic model: claude-3 """ Then listing actors with namespace "local" should return 2 actors And listing actors with namespace "nonexistent" should return 0 actors Scenario: Namespaced name auto-prefixing Given an actor registry with no configured providers for persistence When I add actor from YAML text: """ name: local/auto-prefixed type: llm description: An auto-prefixed actor provider: openai model: gpt-4 """ Then the actor "local/auto-prefixed" should exist Scenario: Get actor by namespaced name Given an actor registry with no configured providers for persistence When I add actor from YAML text: """ name: local/fetchable type: llm description: A fetchable actor provider: openai model: gpt-4 """ Then getting actor "local/fetchable" via registry should return provider "openai" Scenario: Add actor without name raises validation error Given an actor registry with no configured providers for persistence When I attempt to add actor from YAML text without name: """ type: llm description: No name actor provider: openai model: gpt-4 """ Then a validation error should be raised for missing name Scenario: Add duplicate actor without update raises validation error Given an actor registry with no configured providers for persistence When I add actor from YAML text: """ name: local/duplicate type: llm description: A duplicate actor provider: openai model: gpt-4 """ And I attempt to add duplicate actor from YAML text: """ name: local/duplicate type: llm description: A duplicate actor provider: openai model: gpt-4-turbo """ Then a validation error should be raised for duplicate actor Scenario: Upsert actor via legacy API with yaml_text and schema_version Given an actor registry with no configured providers for persistence When I upsert actor "local/legacy-yaml" with yaml_text and schema_version "1.5" Then the actor "local/legacy-yaml" should have yaml_text containing "legacy yaml content" And the actor "local/legacy-yaml" should have schema_version "1.5" # ============================================================ # Originally from: actor_runtime.feature # Feature: Tool-Calling Actor Runtime # ============================================================ Scenario: Tool-call loop with single tool call Given a tool registry with an echo tool And a tool runner for the actor registry And a mock LLM caller that requests one tool call then responds And a tool-calling runtime with the mock LLM caller When I run the tool loop with prompt "Use the echo tool" Then the run result content should be "Final answer after tool call" And the run result should have 1 tool call records And the run result iterations should be 2 And the run result should not be terminated by limit # ---- Multiple Sequential Tool Calls ---- Scenario: Tool-call loop with multiple sequential tool calls Given a tool registry with an echo tool And a tool registry with an adder tool And a tool runner for the actor registry And a mock LLM caller that requests two sequential tool calls then responds And a tool-calling runtime with the mock LLM caller When I run the tool loop with prompt "Use both tools" Then the run result content should be "Done after two tool calls" And the run result should have 2 tool call records And the run result iterations should be 3 And the run result should not be terminated by limit # ---- Max Iteration Safety ---- Scenario: Max-iteration safety termination Given a tool registry with an echo tool And a tool runner for the actor registry And a mock LLM caller that always requests tool calls And a tool-calling runtime with max iterations 3 When I run the tool loop with prompt "Keep calling tools" Then the run result should be terminated by limit And the run result iterations should be 3 And the run result should have 3 tool call records # ---- Tool Not Found Error ---- Scenario: Tool call error handling for tool not found Given a tool registry with an echo tool And a tool runner for the actor registry And a mock LLM caller that requests a nonexistent tool then responds And a tool-calling runtime with the mock LLM caller When I run the tool loop with prompt "Call missing tool" Then the run result should have 1 tool call records And the first tool call record should have success False And the first tool call record should have an error message # ---- Tool Execution Error ---- Scenario: Tool call error handling for execution error Given a tool registry with a failing tool And a tool runner for the actor registry And a mock LLM caller that requests the failing tool then responds And a tool-calling runtime with the mock LLM caller When I run the tool loop with prompt "Call failing tool" Then the run result should have 1 tool call records And the first tool call record should have success False And the first tool call record error should contain "RuntimeError" # ---- Metadata Capture ---- Scenario: Tool metadata capture verification Given a tool registry with an echo tool And a tool runner for the actor registry And a mock LLM caller that requests one tool call then responds And a tool-calling runtime with the mock LLM caller When I run the tool loop with prompt "Echo something" Then the first tool call record tool name should be "test/echo" And the first tool call record inputs should not be empty And the first tool call record output should not be empty And the first tool call record duration should be greater than 0 And the first tool call record iteration should be 1 # ---- Empty Tool List ---- Scenario: Empty tool list handling Given an empty actor tool registry And a tool runner for the actor registry And a mock LLM caller that responds immediately without tool calls And a tool-calling runtime with the mock LLM caller When I run the tool loop with prompt "No tools available" Then the run result content should be "No tools needed" And the run result should have 0 tool call records And the run result iterations should be 1 # ---- Sandbox Root Threading ---- Scenario: Sandbox root threading into tool inputs Given a tool registry with an input-capturing tool And a tool runner for the actor registry And a mock LLM caller that requests the capturing tool then responds And a tool-calling runtime with the mock LLM caller And an actor context with sandbox root "/tmp/sandbox" When I run the tool loop with prompt "Use sandbox" and the actor context Then the captured tool inputs should contain sandbox root "/tmp/sandbox" # ---- Resource Binding Threading ---- Scenario: Resource bindings threading into tool inputs Given a tool registry with an input-capturing tool And a tool runner for the actor registry And a mock LLM caller that requests the capturing tool then responds And a tool-calling runtime with the mock LLM caller And an actor context with resource bindings When I run the tool loop with prompt "Use resources" and the actor context Then the captured tool inputs should contain resource bindings # ---- Constructor Validation ---- Scenario: Runtime rejects invalid max iterations Given a tool registry with an echo tool And a tool runner for the actor registry And a mock LLM caller that responds immediately without tool calls Then creating a runtime with max iterations 0 should raise ValueError Scenario: Runtime rejects empty prompt Given a tool registry with an echo tool And a tool runner for the actor registry And a mock LLM caller that responds immediately without tool calls And a tool-calling runtime with the mock LLM caller Then running the tool loop with empty prompt should raise ValueError # ---- Actor Context ---- Scenario: ToolActorContext construction and properties Given an actor context with plan "plan-abc" phase "execute" Then the actor context plan id should be "plan-abc" And the actor context phase should be "execute" And the actor context tool call history should be empty Scenario: ToolActorContext records tool calls Given an actor context with plan "plan-rec" When I record a tool call with name "test/echo" and success True Then the actor context tool call history should have 1 records Scenario: ToolActorContext rejects empty plan id Then creating a tool actor context with empty plan id should raise ValueError Scenario: ToolActorContext summary includes metadata Given an actor context with plan "plan-sum" sandbox "/tmp/s" Then the actor context summary should contain plan id "plan-sum" And the actor context summary should contain sandbox root "/tmp/s" # ---- Router Integration ---- Scenario: Tool-call loop with router integration Given a tool registry with an echo tool And a tool runner for the actor registry And a tool call router with plan id "plan-r" And a mock LLM caller that requests one tool call then responds And a tool-calling runtime with the mock LLM caller and router When I run the tool loop with prompt "Use echo with router" Then the run result content should be "Final answer after tool call" And the run result should have 1 tool call records # ---- ToolCallRecord Model ---- Scenario: ToolCallRecord model fields Given a tool call record with name "test/tool" and duration 42.5 Then the tool call record tool name should be "test/tool" And the tool call record duration should be 42.5 # ---- Default Context ---- Scenario: Tool loop creates default context when none provided Given a tool registry with an echo tool And a tool runner for the actor registry And a mock LLM caller that responds immediately without tool calls And a tool-calling runtime with the mock LLM caller When I run the tool loop with prompt "No context" and no context Then the run result content should be "No tools needed" And the run result should have 0 tool call records # ---- Actor Context Additional Properties ---- Scenario: ToolActorContext exposes all property accessors Given an actor context with all optional fields Then the actor context automation profile should be "auto-profile" And the actor context project resources should have key "repo" And the actor context metadata should have key "version" And the actor context clear history should empty the history Scenario: ToolActorContext rejects non-ToolCallRecord in record_tool_call Given an actor context with plan "plan-typecheck" Then recording a non-ToolCallRecord should raise TypeError # ---- Runtime Constructor Type Validation ---- Scenario: Runtime rejects invalid registry type Then creating a runtime with a non-ToolRegistry should raise TypeError Scenario: Runtime rejects invalid runner type Given a tool registry with an echo tool Then creating a runtime with a non-ToolRunner should raise TypeError # ---- Runtime Property Accessors ---- Scenario: Runtime exposes max_iterations and provider_format properties Given a tool registry with an echo tool And a tool runner for the actor registry And a mock LLM caller that responds immediately without tool calls And a tool-calling runtime with max iterations 5 Then the runtime max iterations should be 5 And the runtime provider format should not be None # ---- Generic Exception in Tool Execution ---- Scenario: Tool call handles unexpected generic exception Given a tool registry with a tool that raises a generic exception And a tool runner for the actor registry And a mock LLM caller that requests the generic-error tool then responds And a tool-calling runtime with the mock LLM caller When I run the tool loop with prompt "Trigger generic error" Then the run result should have 1 tool call records And the first tool call record should have success False And the first tool call record error should contain "KeyError" # ============================================================ # Originally from: m2_actor_tool_smoke.feature # Feature: M2 actor + tool source smoke suite # ============================================================ Scenario: Load hierarchical actor from YAML fixture Given I load the M2 hierarchical actor fixture Then the M2 actor name should be "m2test/hierarchical-workflow" And the M2 actor type should be "graph" And the M2 actor should have 3 graph nodes And the M2 actor entry node should be "planner" And the M2 actor exit nodes should include "reviewer" Scenario: Load hierarchical actor via ActorLoader from fixture directory Given I create an M2 actor loader from the fixture directory When I run M2 actor discovery Then the M2 loader should find 1 actors And the M2 loader should contain actor "m2test/hierarchical-workflow" # ── Skill pack loading and registry ────────────────────────────── Scenario: Load skill pack from YAML fixture Given I load the M2 skill pack fixture Then the M2 skill name should be "m2test/file-ops-pack" And the M2 skill should have 2 tool references And the M2 skill should have 1 inline tools Scenario: Register skill pack and verify tool resolution Given I register the M2 skill pack in a skill registry When I resolve tools for M2 skill "m2test/file-ops-pack" Then the M2 resolved tools should include a tool_ref entry And the M2 resolved tools should include an inline entry Scenario: Skill registry lists registered skills correctly Given I register the M2 skill pack in a skill registry When I list all M2 skills in the registry Then the M2 skill list should contain "m2test/file-ops-pack" And the M2 skill list should have 1 entries Scenario: Invoke built-in tool through skill context Given I create an M2 skill context with sandbox When I register a tool invocation for "builtin/read_file" in the M2 context Then the M2 skill context should have 1 invocation records # ── Tool lifecycle ─────────────────────────────────────────────── Scenario: Tool lifecycle discover activate execute deactivate Given I create an M2 tool runtime with a mock tool "m2test/echo" And I create an M2 execution context with plan "m2-plan-001" When I discover M2 tool "m2test/echo" Then the M2 discovered descriptor name should be "m2test/echo" When I activate M2 tool "m2test/echo" And I execute M2 tool "m2test/echo" with params text "hello" Then the M2 execution result should be successful When I deactivate M2 tool "m2test/echo" Then the M2 tool should be deactivated Scenario: Tool registry registers and retrieves tools Given I create an M2 tool registry When I register an M2 tool spec "m2test/file-reader" Then getting M2 tool "m2test/file-reader" should return a spec And listing M2 tools should include "m2test/file-reader" # ── MCP stub tool discovery and invocation ─────────────────────── Scenario: MCP stub tool discovery and invocation Given I start the M2 MCP stub server When I discover tools from the M2 MCP stub Then the M2 stub should expose 3 tools And the M2 stub tools should include "mcp/search" And the M2 stub tools should include "mcp/fetch" And the M2 stub tools should include "mcp/transform" When I invoke M2 MCP stub tool "mcp/search" with query "test" Then the M2 MCP stub result should contain results And I stop the M2 MCP stub server Scenario: MCP stub server lifecycle guards Given I have an M2 MCP stub server that is stopped When I try to discover tools from the stopped M2 stub Then an M2 RuntimeError should be raised When I start and invoke M2 MCP stub tool "mcp/fetch" with url "http://example.com" Then the M2 MCP stub fetch result should have status 200 And the M2 MCP stub invocation log should have 1 entries