Feature: Tool Runtime Core As a developer I want a tool runtime with registry, runner, and lifecycle management So that tools can be registered, discovered, activated, executed, and deactivated # ---- Tool Registration ---- Scenario: Register a tool in the registry Given a tool registry And a tool spec named "test/echo" with a handler When I register the tool spec Then the tool should be in the registry Scenario: Get a registered tool by name Given a tool registry And a registered tool spec named "test/echo" When I get the tool by name "test/echo" Then the retrieved tool spec name should be "test/echo" Scenario: List all tools in the registry Given a tool registry And a registered tool spec named "test/alpha" And a registered tool spec named "test/beta" When I list all tools Then the tool list should contain 2 tools Scenario: List tools filtered by namespace Given a tool registry And a registered tool spec named "ns1/tool-a" And a registered tool spec named "ns2/tool-b" And a registered tool spec named "ns1/tool-c" When I list tools with namespace "ns1" Then the tool list should contain 2 tools Scenario: Remove a tool from the registry Given a tool registry And a registered tool spec named "test/remove-me" When I remove the tool "test/remove-me" Then the removal should succeed And getting "test/remove-me" should return None # ---- Missing Tool Errors ---- Scenario: Get a missing tool returns None Given a tool registry When I get the tool by name "test/nonexistent" Then the retrieved tool spec should be None Scenario: Remove a missing tool returns False Given a tool registry When I remove the tool "test/nonexistent" Then the removal should fail # ---- Name Collision Detection ---- Scenario: Duplicate registration raises ToolError Given a tool registry And a registered tool spec named "test/duplicate" When I try to register a duplicate tool "test/duplicate" Then a ToolError should be raised with type "RegistrationError" # ---- Capability Flag Validation ---- Scenario: Tool spec with read-only capability Given a tool spec named "test/reader" with read_only capability Then the tool spec capabilities read_only should be True And the tool spec capabilities writes should be False Scenario: Tool spec with write capability Given a tool spec named "test/writer" with writes capability Then the tool spec capabilities writes should be True # ---- Tool Lifecycle Hook Ordering ---- Scenario: Full lifecycle discover activate execute deactivate Given a tool registry And a registered tool spec named "test/lifecycle" And a tool runner for the registry When I discover tools from the runner And I activate tool "test/lifecycle" And I execute tool "test/lifecycle" with inputs {"key": "value"} And I deactivate tool "test/lifecycle" Then the lifecycle should complete in order # ---- Execute With Valid Inputs ---- Scenario: Execute with valid inputs returns success Given a tool registry And a registered tool spec named "test/add" with an adder handler And a tool runner for the registry When I activate tool "test/add" And I execute tool "test/add" with inputs {"a": 1, "b": 2} Then the tool result should be successful And the tool result output should contain key "sum" And the tool result duration_ms should be non-negative # ---- Execute With Handler Failure ---- Scenario: Execute with handler failure returns failure result Given a tool registry And a registered tool spec named "test/fail" with a failing handler And a tool runner for the registry When I execute tool "test/fail" with inputs {} Then the tool result should not be successful And the tool result error should not be empty # ---- Thread-Safe Concurrent Registration ---- Scenario: Concurrent registrations are thread-safe Given a tool registry When I concurrently register 50 tools Then all 50 tools should be in the registry # ---- JSON-Serialisable IO Validation ---- Scenario: Execute rejects non-JSON-serialisable inputs Given a tool registry And a registered tool spec named "test/json-check" And a tool runner for the registry When I execute tool "test/json-check" with non-serialisable inputs Then the tool result should not be successful And the tool result error should mention "JSON" Scenario: Execute rejects non-JSON-serialisable outputs Given a tool registry And a registered tool spec named "test/bad-output" with a non-serialisable handler And a tool runner for the registry When I execute tool "test/bad-output" with inputs {} Then the tool result should not be successful And the tool result error should mention "JSON" # ---- ToolError Exception ---- Scenario: ToolError carries structured context When I create a ToolError with name "test/err" type "TestError" and details "something broke" Then the ToolError tool_name should be "test/err" And the ToolError error_type should be "TestError" And the ToolError details should be "something broke" And the ToolError message should contain "TestError" # ---- ToolResult Model ---- Scenario: ToolResult with success and metadata When I create a ToolResult with success True and metadata Then the ToolResult success should be True And the ToolResult metadata should not be empty Scenario: ToolResult with failure When I create a ToolResult with success False and error "handler crashed" Then the ToolResult success should be False And the ToolResult error should be "handler crashed" # ---- Runner Activate Errors ---- Scenario: Activate missing tool raises ToolError Given a tool registry And a tool runner for the registry When I try to activate a missing tool "test/missing" Then a ToolError should be raised with type "ActivationError" # ---- Runner Execute Missing Tool ---- Scenario: Execute missing tool raises ToolError Given a tool registry And a tool runner for the registry When I try to execute a missing tool "test/missing" with inputs {} Then a ToolError should be raised with type "ExecutionError" # ---- Runner Deactivate ---- Scenario: Deactivate a non-active tool returns False Given a tool registry And a tool runner for the registry When I deactivate tool "test/nonexistent" Then the deactivation should return False # ---- Discover With Alternate Registry ---- Scenario: Discover from an alternate registry Given a tool registry And a tool runner for the registry And an alternate registry with 3 tools When I discover tools from the alternate registry Then the discovered list should contain 3 tools # ---- Execute Without Prior Activate ---- Scenario: Execute without prior activate still works via registry lookup Given a tool registry And a registered tool spec named "test/no-activate" with an adder handler And a tool runner for the registry When I execute tool "test/no-activate" with inputs {"a": 5, "b": 3} Then the tool result should be successful # ---- Handler Returns Non-Dict ---- Scenario: Handler returning non-dict is wrapped in result dict Given a tool registry And a registered tool spec named "test/scalar" with a scalar handler And a tool runner for the registry When I execute tool "test/scalar" with inputs {} Then the tool result should be successful And the tool result output should contain key "result" # ---- List Tools With tool_type Filter ---- Scenario: List tools with tool_type filter Given a tool registry And a registered tool spec named "test/filtered" When I list tools with tool_type "tool" Then the tool list should contain 1 tools