Feature: Consolidated Skill
  Combined scenarios from: skill_context, skill_discovery, skill_flatten, skill_inline_coverage, skill_protocol, skill_resolution

  # ============================================================
  # Originally from: skill_context.feature
  # Feature: Skill Context and Registry
  # ============================================================

  Scenario: Create SkillContext with plan/project/sandbox info
    When I create a skill_context with plan "plan-001" project "proj-alpha" and sandbox "/tmp/sandbox"
    Then the skill_context should be created
    And the skill_context plan_id should be "plan-001"
    And the skill_context project_id should be "proj-alpha"
    And the skill_context sandbox_path should be "/tmp/sandbox"
    And the skill_context should not be read_only


  Scenario: Create read-only SkillContext
    When I create a read_only skill_context with plan "plan-002" project "proj-beta"
    Then the skill_context should be created
    And the skill_context should be read_only


  Scenario: SkillContext with custom metadata
    When I create a skill_context with metadata key "env" value "staging"
    Then the skill_context plan metadata should contain key "env"
    And the skill_context plan metadata key "env" should be "staging"

    # ---- Resource Resolution ----


  Scenario: Resolve bound resources from context
    When I create a skill_context with resource "git-checkout" bound to "/repo/checkout"
    And I resolve resource "git-checkout" from the skill_context
    Then the resolved resource should be "/repo/checkout"


  Scenario: Resolve missing resource raises SkillError
    When I create a skill_context with no resources
    And I try to resolve resource "missing-resource" from the skill_context
    Then a skill_context resolution error should be raised

    # ---- Change Tracking ----


  Scenario: Register tool invocation in change tracker
    When I create a skill_context with plan "plan-003" project "proj-gamma" and sandbox "/tmp/sb"
    And I register a tool invocation for "local/edit-file" with duration 42.5
    Then the skill_context change tracker should have 1 records
    And the last change tracker record tool_name should be "local/edit-file"
    And the last change tracker record duration_ms should be 42.5


  Scenario: Multiple tool invocations are tracked in order
    When I create a skill_context with plan "plan-004" project "proj-delta" and sandbox "/tmp/sb2"
    And I register a tool invocation for "local/read-file" with duration 10.0
    And I register a tool invocation for "local/write-file" with duration 20.0
    Then the skill_context change tracker should have 2 records

    # ---- Write Guard ----


  Scenario: Enforce read-only write guard raises SkillError
    When I create a read_only skill_context with plan "plan-005" project "proj-epsilon"
    And I try to enforce write guard for tool "local/write-file"
    Then a skill_context permission denied error should be raised
    And the skill_context permission error tool_name should be "local/write-file"


  Scenario: Write guard passes in writable context
    When I create a skill_context with plan "plan-006" project "proj-zeta" and sandbox "/tmp/sb3"
    And I enforce write guard for tool "local/write-file" in writable context
    Then no skill_context error should be raised

    # ---- Plan Metadata ----


  Scenario: Get plan metadata includes plan_id and project_id
    When I create a skill_context with plan "plan-007" project "proj-eta" and sandbox "/tmp/sb4"
    Then the skill_context plan metadata should contain key "plan_id"
    And the skill_context plan metadata key "plan_id" should be "plan-007"
    And the skill_context plan metadata should contain key "project_id"
    And the skill_context plan metadata key "project_id" should be "proj-eta"

    # ---- SkillRegistry Register/Get/List/Unregister ----


  Scenario: SkillRegistry register and get skill
    When I create a skill_registry
    And I register a skill "local/test-reg" in the registry
    And I get skill "local/test-reg" from the registry
    Then the registry skill name should be "local/test-reg"


  Scenario: SkillRegistry register duplicate raises error
    When I create a skill_registry
    And I register a skill "local/dup-skill" in the registry
    And I try to register a duplicate skill "local/dup-skill"
    Then a skill_context validation error should be raised


  Scenario: SkillRegistry get missing skill raises error
    When I create a skill_registry
    And I try to get skill "local/missing" from the registry
    Then a skill_context not found error should be raised


  Scenario: SkillRegistry list_all returns metadata
    When I create a skill_registry
    And I register a skill "local/skill-a" in the registry
    And I register a skill "local/skill-b" in the registry
    And I list all skills from the registry
    Then the registry should have 2 skills
    And the registry skill list should contain "local/skill-a"
    And the registry skill list should contain "local/skill-b"


  Scenario: SkillRegistry unregister removes skill
    When I create a skill_registry
    And I register a skill "local/to-remove" in the registry
    And I unregister skill "local/to-remove" from the registry
    And I try to get skill "local/to-remove" from the registry
    Then a skill_context not found error should be raised


  Scenario: SkillRegistry unregister missing skill raises error
    When I create a skill_registry
    And I try to unregister skill "local/nonexistent" from the registry
    Then a skill_context not found error should be raised

    # ---- SkillRegistry Tool Resolution ----


  Scenario: SkillRegistry resolve_tools through tool references
    When I create a skill_registry
    And I register a skill "local/resolve-test" with tool refs in the registry
    And I resolve tools for "local/resolve-test" from the registry
    Then the resolved tools should have 2 entries
    And the resolved tools should contain "local/tool-a"
    And the resolved tools should contain "local/tool-b"

    # ---- SkillRegistry Validation ----


  @tdd_issue @tdd_issue_4241 @tdd_expected_fail
  Scenario: SkillRegistry validate_skill detects missing tools
    When I create a skill_registry with a mock tool registry
    And I validate a skill with missing tool refs
    Then the validation errors should contain "not found in tool registry"


  Scenario: SkillRegistry validate_skill detects missing includes
    When I create a skill_registry
    And I validate a skill with missing includes
    Then the validation errors should contain "is not registered"


  Scenario: SkillRegistry validate_skill returns empty for valid skill
    When I create a skill_registry
    And I validate a skill with no issues
    Then the validation errors should be empty


  # ============================================================
  # Originally from: skill_discovery.feature
  # Feature: Agent Skills Discovery
  # ============================================================

  Scenario: Config key skills.agent_skills_paths is registered
    Given the agent skills discovery module is available
    When I check the config registry for "skills.agent_skills_paths"
    Then the config entry should exist
    And the config entry type should be "str"
    And the config entry env var should be "CLEVERAGENTS_SKILLS_AGENT_SKILLS_PATHS"

    # --- Path parsing ---


  Scenario: Parse single agent skills path
    Given the agent skills discovery module is available
    When I parse agent skills paths "~/.cleveragents/agent_skills"
    Then the result should contain 1 path


  Scenario: Parse multiple comma-separated paths
    Given the agent skills discovery module is available
    When I parse agent skills paths "/opt/skills,/home/user/skills"
    Then the result should contain 2 paths


  Scenario: Parse empty agent skills paths
    Given the agent skills discovery module is available
    When I parse an empty agent skills path string
    Then the result should contain 0 paths


  Scenario: Parse paths with whitespace around commas
    Given the agent skills discovery module is available
    When I parse agent skills paths "/opt/skills , /home/user/skills"
    Then the result should contain 2 paths

    # --- Discovery ---


  Scenario: Discover agent skills from directory with SKILL.md
    Given the agent skills discovery module is available
    Given a temporary directory with agent skill folders
      | folder_name | skill_name | description          |
      | my-tool     | my-tool    | A test agent skill   |
    When I scan the directory for agent skills
    Then I should discover 1 agent skill
    And the discovered skill "my-tool" should have description "A test agent skill"


  Scenario: Discover multiple agent skills
    Given the agent skills discovery module is available
    Given a temporary directory with agent skill folders
      | folder_name | skill_name  | description          |
      | tool-a      | tool-a      | First tool           |
      | tool-b      | tool-b      | Second tool          |
      | tool-c      | tool-c      | Third tool           |
    When I scan the directory for agent skills
    Then I should discover 3 agent skills


  Scenario: Skip directories without SKILL.md
    Given the agent skills discovery module is available
    Given a temporary directory with some folders missing SKILL.md
    When I scan the directory for agent skills
    Then I should discover 0 agent skills


  Scenario: Handle non-existent directory gracefully
    Given the agent skills discovery module is available
    When I run discovery on a non-existent directory
    Then the discovery result should have 0 discovered skills
    And the discovery result should have errors mentioning "does not exist"


  Scenario: Handle SKILL.md without front-matter
    Given the agent skills discovery module is available
    Given a temporary directory with a SKILL.md that has no front-matter
    When I scan the directory for agent skills
    Then I should discover 0 agent skills


  Scenario: Fall back to folder name when SKILL.md has no name field
    Given the agent skills discovery module is available
    Given a temporary directory with agent skill folders
      | folder_name | skill_name | description      |
      | fallback-tool | _none_   | Some description |
    When I scan the directory for agent skills
    Then I should discover 1 agent skill
    And the discovered skill should use folder name as name

    # --- ToolSpec building ---


  Scenario: Build ToolSpec from discovered agent skill
    Given the agent skills discovery module is available
    Given a discovered agent skill named "my-tool" at "/opt/skills/my-tool"
    When I build a ToolSpec from the discovered skill
    Then the ToolSpec name should be "agent_skills/my-tool"
    And the ToolSpec source should be "agent_skills"
    And the ToolSpec source_metadata should contain path "/opt/skills/my-tool"

    # --- Registration ---


  Scenario: Register discovered agent skills in ToolRegistry
    Given the agent skills discovery module is available
    Given a ToolRegistry with no existing tools
    And a list of 2 discovered agent skills
    When I register discovered skills with "skip" conflict strategy
    Then 2 tools should be registered in the ToolRegistry
    And 0 conflicts should be reported


  Scenario: Skip registration on name collision with skip strategy
    Given the agent skills discovery module is available
    Given a ToolRegistry with an existing tool "agent_skills/collider"
    And a discovered agent skill named "collider"
    When I register discovered skills with "skip" conflict strategy
    Then 0 tools should be registered in the ToolRegistry
    And 1 conflict should be reported with tool name "agent_skills/collider"


  Scenario: Error on name collision with error strategy
    Given the agent skills discovery module is available
    Given a ToolRegistry with an existing tool "agent_skills/collider"
    And a discovered agent skill named "collider"
    When I register discovered skills with "error" conflict strategy
    Then a discovery ValueError should be raised mentioning "name collision"


  Scenario: Replace existing tool on collision with replace strategy
    Given the agent skills discovery module is available
    Given a ToolRegistry with an existing tool "agent_skills/collider"
    And a discovered agent skill named "collider"
    When I register discovered skills with "replace" conflict strategy
    Then 1 tool should be registered in the ToolRegistry
    And the tool "agent_skills/collider" should have source "agent_skills"

    # --- Refresh ---


  Scenario: Refresh removes old agent skills and re-discovers
    Given the agent skills discovery module is available
    Given a SkillRegistryService with a ToolRegistry
    And agent skills paths pointing to a directory with 2 skills
    When I call discover_and_register
    Then 2 agent skills should be registered
    When the directory now has 3 skills and I call refresh_agent_skills
    Then 3 agent skills should be registered

    # --- Edge cases ---


  Scenario: Register empty discovered list returns empty
    Given the agent skills discovery module is available
    Given a ToolRegistry with no existing tools
    When I register an empty discovered skills list
    Then the registration should return 0 specs and 0 conflicts


  Scenario: Scan non-directory path returns empty
    Given the agent skills discovery module is available
    When I scan a non-directory path for agent skills
    Then I should discover 0 agent skills


  Scenario: Discover with path that is a file not a directory
    Given the agent skills discovery module is available
    When I run discovery on a path that is a file
    Then the discovery result should have 0 discovered skills
    And the discovery result should have errors mentioning "not a directory"


  Scenario: SKILL.md with invalid YAML front-matter
    Given the agent skills discovery module is available
    Given a temporary directory with a SKILL.md that has invalid YAML
    When I scan the directory for agent skills
    Then I should discover 0 agent skills


  Scenario: SKILL.md with empty YAML block
    Given the agent skills discovery module is available
    Given a temporary directory with a SKILL.md that has empty front-matter
    When I scan the directory for agent skills
    Then I should discover 0 agent skills


  Scenario: SKILL.md with non-dict YAML
    Given the agent skills discovery module is available
    Given a temporary directory with a SKILL.md that has non-dict YAML
    When I scan the directory for agent skills
    Then I should discover 0 agent skills


  Scenario: Skill with non-string description in front-matter
    Given the agent skills discovery module is available
    Given a temporary directory with a SKILL.md that has numeric description
    When I scan the directory for agent skills
    Then I should discover 1 agent skill


  Scenario: Skill with empty description falls back to default
    Given the agent skills discovery module is available
    Given a temporary directory with a SKILL.md that has empty description
    When I scan the directory for agent skills
    Then I should discover 1 agent skill
    And the first discovered skill description should contain "Agent skill from"


  Scenario: Noop handler returns expected placeholder
    Given the agent skills discovery module is available
    When I call the noop handler
    Then the noop handler should return status "agent_skill_placeholder"

    # --- Source metadata ---


  Scenario: Source metadata is included when resolving skill tools
    Given the agent skills discovery module is available
    Given a registered skill "local/test-skill" with an agent_skill source
    When I resolve tools for "local/test-skill"
    Then the resolved tools should include agent_skill source entries


  # ============================================================
  # Originally from: skill_flatten.feature
  # Feature: Skill Registry Flattening and Capability Summaries
  # ============================================================

  @flatten_tool_refs
  Scenario: Flattening a skill with named tool refs produces deterministic order
    Given a flatten registry skill "local/basic" with tool refs "local/alpha,local/beta,local/gamma"
    When I flatten the skill "local/basic"
    Then the flatten result should have 3 entries
    And the flatten entry at index 0 should be "local/alpha"
    And the flatten entry at index 1 should be "local/beta"
    And the flatten entry at index 2 should be "local/gamma"

    # ---------------------------------------------------------------------------
    # Flattening with includes (depth-first ordering)
    # ---------------------------------------------------------------------------


  @flatten_includes
  Scenario: Flattening a skill with includes uses depth-first ordering
    Given a flatten registry skill "local/base" with tool refs "local/base-tool"
    And a flatten registry skill "local/mid" including "local/base" with refs "local/mid-tool"
    And a flatten registry skill "local/top" including "local/mid" with refs "local/top-tool"
    When I flatten the skill "local/top"
    Then the flatten result should have 3 entries
    And the flatten entry at index 0 should be "local/base-tool"
    And the flatten entry at index 1 should be "local/mid-tool"
    And the flatten entry at index 2 should be "local/top-tool"

    # ---------------------------------------------------------------------------
    # Flattening with inline tools
    # ---------------------------------------------------------------------------


  @flatten_inline
  Scenario: Flattening a skill with inline tools includes anonymous entries
    Given a flatten registry skill "local/inline-skill" with 2 inline tools
    When I flatten the skill "local/inline-skill"
    Then the flatten result should have 2 entries
    And the flatten entry at index 0 should be "local/inline-skill/_anon_0"
    And the flatten entry at index 1 should be "local/inline-skill/_anon_1"
    And the flatten entry at index 0 should be marked inline

    # ---------------------------------------------------------------------------
    # Cycle detection with clear error path
    # ---------------------------------------------------------------------------


  @flatten_cycle
  Scenario: Flattening detects cycles and reports a clear path
    Given a flatten registry skill "local/cycle-a" including "local/cycle-b" with no refs
    And a flatten registry skill "local/cycle-b" including "local/cycle-a" with no refs
    When I try to flatten the skill "local/cycle-a"
    Then a flatten cycle error should be raised
    And the flatten error should mention "local/cycle-a"
    And the flatten error should mention "local/cycle-b"

    # ---------------------------------------------------------------------------
    # Per-include override application
    # ---------------------------------------------------------------------------


  @flatten_include_overrides
  Scenario: Per-include overrides are applied to included tool entries
    Given a flatten registry skill "local/child" with tool refs "local/tool-x"
    And a flatten registry skill "local/parent" including "local/child" with overrides timeout 600
    When I flatten the skill "local/parent"
    Then the flatten result should have 1 entries
    And the flatten entry "local/tool-x" should have override "timeout" equal to 600

    # ---------------------------------------------------------------------------
    # Non-overridable field rejection
    # ---------------------------------------------------------------------------


  @flatten_non_overridable
  Scenario: Non-overridable fields are rejected with clear error
    Given a flatten registry skill "local/child-nr" with tool refs "local/tool-y"
    And a flatten registry skill "local/parent-nr" including "local/child-nr" with overrides on non-overridable field "name"
    When I try to flatten the skill "local/parent-nr"
    Then a flatten override error should be raised
    And the flatten error should mention "name"
    And the flatten error should mention "Non-overridable"

    # ---------------------------------------------------------------------------
    # Capability summary computation
    # ---------------------------------------------------------------------------


  @flatten_capability_summary
  Scenario: Capability summary aggregates read/write/checkpoint/side-effect flags
    Given a flatten registry skill "local/cap-skill" with mixed capability inline tools
    When I flatten the skill "local/cap-skill" and compute summary
    Then the flatten summary total_tools should be 3
    And the flatten summary read_only_tools should be 1
    And the flatten summary write_tools should be 1
    And the flatten summary has_side_effects should be true
    And the flatten summary checkpointable_tools should be 2

    # ---------------------------------------------------------------------------
    # tools() method returns both entries and summary
    # ---------------------------------------------------------------------------


  @flatten_tools_method
  Scenario: The tools() method returns entries and capability summary
    Given a flatten skill registry with skill "local/tools-test" having 3 refs
    When I call the flatten tools method for "local/tools-test"
    Then the flatten tools result should contain 3 entries
    And the flatten tools result should include a capability summary

    # ---------------------------------------------------------------------------
    # validate_plan() with valid plan
    # ---------------------------------------------------------------------------


  @flatten_validate_plan
  Scenario: validate_plan with a valid plan returns no errors
    Given a flatten skill registry with skill "local/plan-skill" having 2 refs
    When I call flatten validate_plan with skills "local/plan-skill"
    Then the flatten validation result should have 0 errors

    # ---------------------------------------------------------------------------
    # validate_plan() with missing skill
    # ---------------------------------------------------------------------------


  @flatten_validate_plan_missing
  Scenario: validate_plan with a missing skill reports error
    Given an empty flatten skill registry
    When I call flatten validate_plan with skills "local/nonexistent"
    Then the flatten validation result should have 1 errors
    And the flatten validation error should mention "local/nonexistent"

    # ---------------------------------------------------------------------------
    # validate_plan() with cycle in includes
    # ---------------------------------------------------------------------------


  @flatten_validate_plan_cycle
  Scenario: validate_plan detects cycle in includes
    Given a flatten registry skill "local/loop-a" including "local/loop-b" with no refs
    And a flatten registry skill "local/loop-b" including "local/loop-a" with no refs
    And the flatten registry skills are registered in the skill registry
    When I call flatten validate_plan with skills "local/loop-a"
    Then the flatten validation result should have 1 errors
    And the flatten validation error should mention "ycle"

    # ---------------------------------------------------------------------------
    # De-duplication semantics (last-wins)
    # ---------------------------------------------------------------------------


  @flatten_dedup
  Scenario: De-duplication uses last-wins for entry metadata
    Given a flatten registry skill "local/dup-a" with tool refs "local/shared"
    And a flatten registry skill "local/dup-b" with tool refs "local/shared"
    And a flatten registry skill "local/dup-top" including "local/dup-a,local/dup-b" with no refs
    When I flatten the skill "local/dup-top"
    Then the flatten result should have 1 entries
    And the flatten entry "local/shared" source_skill should be "local/dup-b"

    # ---------------------------------------------------------------------------
    # Override merging (shallow merge)
    # ---------------------------------------------------------------------------


  @flatten_override_merge
  Scenario: Override merging uses shallow merge semantics
    Given a flatten registry skill "local/merge-child" with tool refs "local/merge-tool" and overrides priority 1
    And a flatten registry skill "local/merge-parent" including "local/merge-child" with overrides priority 2 and extra key
    When I flatten the skill "local/merge-parent"
    Then the flatten entry "local/merge-tool" should have override "priority" equal to 2
    And the flatten entry "local/merge-tool" should have override "extra" equal to "yes"


  # ============================================================
  # Originally from: skill_inline_coverage.feature
  # Feature: Inline Tool Executor Coverage Boost
  # ============================================================

  Scenario: Executor rejects non-positive max_runtime_seconds
    Given I create an inline executor with invalid max runtime
    Then creating the executor should raise a ValueError about runtime


  Scenario: Executor rejects non-positive max_output_bytes
    Given I create an inline executor with invalid max output
    Then creating the executor should raise a ValueError about output

    # ---- Property Accessors ----


  Scenario: Executor exposes max_runtime_seconds property
    Given an inline executor with max runtime 5.0 seconds
    Then the executor max_runtime_seconds should be 5.0


  Scenario: Executor exposes max_output_bytes property
    Given an inline executor with max output 512 bytes
    Then the executor max_output_bytes should be 512

    # ---- Null Argument Guards ----


  Scenario: Execute rejects None tool argument
    Given a writable skill context with sandbox "/tmp/inline-sandbox"
    And an inline executor with max runtime 5.0 seconds
    When I execute with None tool
    Then executing should raise a ValueError about tool


  Scenario: Execute rejects None context argument
    Given an inline tool with code that prints "test"
    When I execute with None context
    Then executing should raise a ValueError about context


  Scenario: Execute rejects None input_data argument
    Given an inline tool with code that prints "test"
    And a writable skill context with sandbox "/tmp/inline-sandbox"
    When I execute with None input_data
    Then executing should raise a ValueError about input_data


  Scenario: Validate_tool rejects None tool argument
    Given an inline executor with max runtime 5.0 seconds
    When I validate with None tool
    Then validating should raise a ValueError about tool

    # ---- Validation Failure Return Path ----


  Scenario: Execute returns validation failure without running code
    Given an inline tool with no code
    And a writable skill context with sandbox "/tmp/inline-sandbox"
    When I execute the inline tool
    Then the inline result should not be successful
    And the inline result error message should contain "Validation failed"
    And the inline result duration should be zero

    # ---- Sandbox Path Validation ----


  Scenario: Execute rejects input paths outside sandbox
    Given an inline tool with code that prints "file op"
    And a writable skill context with sandbox "/tmp/inline-sandbox"
    When I execute the inline tool with path input escaping sandbox
    Then the inline result should not be successful
    And the inline result error message should contain "escapes sandbox"


  Scenario: Execute accepts input paths inside sandbox
    Given an inline tool with code that prints "ok"
    And a writable skill context with sandbox "/tmp/inline-sandbox"
    When I execute the inline tool with path input inside sandbox
    Then the inline result should be successful


  Scenario: Execute handles invalid path values gracefully
    Given an inline tool with code that prints "test"
    And a writable skill context with sandbox "/tmp/inline-sandbox"
    When I execute the inline tool with an invalid path value
    Then the inline result should not be successful
    And the inline result error message should contain "Invalid path"


  # ============================================================
  # Originally from: skill_protocol.feature
  # Feature: Skill Protocol Types
  # ============================================================

  Scenario: SkillErrorType has all expected members
    When I list all skill_protocol error types
    Then the skill_protocol error types should include "skill_not_found"
    And the skill_protocol error types should include "resolution_failure"
    And the skill_protocol error types should include "cycle_detected"
    And the skill_protocol error types should include "tool_activation_failure"
    And the skill_protocol error types should include "tool_execution_failure"
    And the skill_protocol error types should include "validation_error"
    And the skill_protocol error types should include "permission_denied"


  Scenario: SkillErrorType has exactly 7 members
    When I list all skill_protocol error types
    Then there should be 7 skill_protocol error types

    # ---- SkillError Creation ----


  Scenario: Create a SkillError with required fields
    When I create a skill_protocol error with type "tool_execution_failure" and message "Tool timed out" for skill "local/my-skill"
    Then the skill_protocol error should be created
    And the skill_protocol error type should be "tool_execution_failure"
    And the skill_protocol error message should be "Tool timed out"
    And the skill_protocol error skill_name should be "local/my-skill"
    And the skill_protocol error tool_name should be none


  Scenario: Create a SkillError with optional tool_name
    When I create a skill_protocol error with tool_name "local/my-tool"
    Then the skill_protocol error should be created
    And the skill_protocol error tool_name should be "local/my-tool"


  Scenario: Create a SkillError with details
    When I create a skill_protocol error with details
    Then the skill_protocol error should be created
    And the skill_protocol error details should have key "trace_id"


  Scenario: SkillError is frozen (immutable)
    When I create a skill_protocol error with type "validation_error" and message "Bad input" for skill "local/test"
    Then the skill_protocol error should be immutable

    # ---- SkillMetadata Creation ----


  Scenario: Create SkillMetadata from Skill domain model
    When I create a skill_protocol metadata from a basic skill
    Then the skill_protocol metadata should be created
    And the skill_protocol metadata name should be "local/test-skill"
    And the skill_protocol metadata description should be "A test skill"
    And the skill_protocol metadata version should be "0.0.0"
    And the skill_protocol metadata read_only should be true
    And the skill_protocol metadata writes should be false


  Scenario: Create SkillMetadata from Skill with tool refs
    When I create a skill_protocol metadata from a skill with tool refs
    Then the skill_protocol metadata should be created
    And the skill_protocol metadata tool_count should be 2
    And the skill_protocol metadata source_types should contain "tool_ref"


  Scenario: Create SkillMetadata from Skill with inline write tools
    When I create a skill_protocol metadata from a skill with write tools
    Then the skill_protocol metadata should be created
    And the skill_protocol metadata writes should be true
    And the skill_protocol metadata read_only should be false


  Scenario: Create SkillMetadata from Skill with MCP sources
    When I create a skill_protocol metadata from a skill with mcp sources
    Then the skill_protocol metadata should be created
    And the skill_protocol metadata source_types should contain "mcp"


  Scenario: Create SkillMetadata from Skill with agent skill sources
    When I create a skill_protocol metadata from a skill with agent skill sources
    Then the skill_protocol metadata should be created
    And the skill_protocol metadata source_types should contain "agent_skill"


  Scenario: Create SkillMetadata with custom version
    When I create a skill_protocol metadata with version "1.2.3"
    Then the skill_protocol metadata should be created
    And the skill_protocol metadata version should be "1.2.3"


  Scenario: SkillMetadata is frozen (immutable)
    When I create a skill_protocol metadata from a basic skill
    Then the skill_protocol metadata should be immutable


  Scenario: SkillMetadata from_skill rejects None
    When I try to create skill_protocol metadata from None
    Then a skill_protocol value error should be raised

    # ---- SkillDefinition ----


  Scenario: Create a SkillDefinition with resolved tools
    When I create a skill_protocol definition with resolved tools
    Then the skill_protocol definition should be created
    And the skill_protocol definition skill name should be "local/def-skill"
    And the skill_protocol definition should have 1 resolved tools


  Scenario: Create a SkillDefinition with input and output schemas
    When I create a skill_protocol definition with input and output schemas
    Then the skill_protocol definition should be created
    And the skill_protocol definition input_schema should have key "type"
    And the skill_protocol definition output_schema should have key "type"


  Scenario: SkillDefinition rejects input_schema without type key
    When I try to create a skill_protocol definition with invalid input_schema
    Then a skill_protocol validation error should be raised


  Scenario: SkillDefinition rejects output_schema without type key
    When I try to create a skill_protocol definition with invalid output_schema
    Then a skill_protocol validation error should be raised


  Scenario: SkillDefinition detects writes inconsistency
    When I try to create a skill_protocol definition with writes inconsistency
    Then a skill_protocol validation error should be raised


  Scenario: SkillDefinition is frozen (immutable)
    When I create a skill_protocol definition with resolved tools
    Then the skill_protocol definition should be immutable

    # ---- SkillResult ----


  Scenario: Create a successful SkillResult
    When I create a skill_protocol result with success
    Then the skill_protocol result should be created
    And the skill_protocol result success should be true
    And the skill_protocol result skill_name should be "local/my-skill"
    And the skill_protocol result tool_name should be "local/my-tool"
    And the skill_protocol result error should be none
    And the skill_protocol result output_data should be "hello"


  Scenario: Create a failed SkillResult with error
    When I create a skill_protocol result with failure
    Then the skill_protocol result should be created
    And the skill_protocol result success should be false
    And the skill_protocol result error should not be none
    And the skill_protocol result error type should be "tool_execution_failure"


  Scenario: Create a SkillResult with duration and resource changes
    When I create a skill_protocol result with duration and resource changes
    Then the skill_protocol result should be created
    And the skill_protocol result duration_ms should be 150.5
    And the skill_protocol result should have 1 resource changes


  Scenario: SkillResult is frozen (immutable)
    When I create a skill_protocol result with success
    Then the skill_protocol result should be immutable

    # ---- Error Mapping ----


  Scenario: Map ValueError with cycle message to CYCLE_DETECTED
    When I map a skill_protocol ValueError with message "Cycle detected in skill includes"
    Then the skill_protocol mapped error type should be "cycle_detected"


  Scenario: Map ValueError with not found message to SKILL_NOT_FOUND
    When I map a skill_protocol ValueError with message "Skill not found"
    Then the skill_protocol mapped error type should be "skill_not_found"


  Scenario: Map generic ValueError to RESOLUTION_FAILURE
    When I map a skill_protocol ValueError with message "Something went wrong"
    Then the skill_protocol mapped error type should be "resolution_failure"


  Scenario: Map PermissionError to PERMISSION_DENIED
    When I map a skill_protocol PermissionError
    Then the skill_protocol mapped error type should be "permission_denied"


  Scenario: Map RuntimeError to TOOL_EXECUTION_FAILURE
    When I map a skill_protocol RuntimeError with message "Unexpected failure"
    Then the skill_protocol mapped error type should be "tool_execution_failure"


  Scenario: Map RuntimeError with activation in message
    When I map a skill_protocol RuntimeError with message "activation failed"
    Then the skill_protocol mapped error type should be "tool_activation_failure"


  Scenario: Error mapping with tool_name populated
    When I map a skill_protocol error with tool_name "local/my-tool"
    Then the skill_protocol mapped error tool_name should be "local/my-tool"
    And the skill_protocol mapped error details should have key "tool_name"


  Scenario: Error mapping rejects empty skill_name
    When I try to map a skill_protocol error with empty skill_name
    Then a skill_protocol value error should be raised

    # ---- Writes/Read-Only Metadata Propagation ----


  Scenario: Skill with only read-only tools has read_only=True
    When I create a skill_protocol metadata from a skill with read_only tools
    Then the skill_protocol metadata read_only should be true
    And the skill_protocol metadata writes should be false


  Scenario: Skill with write tools has writes=True
    When I create a skill_protocol metadata from a skill with write tools
    Then the skill_protocol metadata writes should be true
    And the skill_protocol metadata read_only should be false


  Scenario: Empty skill has read_only=True
    When I create a skill_protocol metadata from an empty skill
    Then the skill_protocol metadata read_only should be true
    And the skill_protocol metadata writes should be false
    And the skill_protocol metadata tool_count should be 0


  # ============================================================
  # Originally from: skill_resolution.feature
  # Feature: Skill Domain Model and Resolution
  # ============================================================

  Scenario: Create a skill with valid fields
    When I create a skill_model with name "local/code-tools" and description "Code editing tools"
    Then the skill_model should be created
    And the skill_model name should be "local/code-tools"
    And the skill_model description should be "Code editing tools"
    And the skill_model namespace should be "local"
    And the skill_model short_name should be "code-tools"


  Scenario: Create a skill with tool refs
    When I create a skill_model with tool refs "local/edit-file,local/read-file"
    Then the skill_model should be created
    And the skill_model should have 2 tool refs


  Scenario: Create a skill with all fields populated
    When I create a skill_model with all fields
    Then the skill_model should be created
    And the skill_model should have 2 tool refs
    And the skill_model should have 1 include
    And the skill_model should have 1 anonymous tool
    And the skill_model should have 1 mcp server
    And the skill_model should have 1 agent skill

    # ---- Skill Name Validation ----


  Scenario: Skill name must match namespace/name pattern
    When I try to create a skill_model with invalid name "no-namespace"
    Then a skill_model validation error should be raised
    And the skill_model error should mention "namespace/name"


  Scenario: Skill name rejects spaces
    When I try to create a skill_model with invalid name "local/bad name"
    Then a skill_model validation error should be raised


  Scenario: Skill name rejects double slashes
    When I try to create a skill_model with invalid name "local//bad"
    Then a skill_model validation error should be raised


  Scenario: Skill name allows hyphens and underscores
    When I create a skill_model with name "my-ns/my_skill" and description "Test"
    Then the skill_model should be created
    And the skill_model namespace should be "my-ns"
    And the skill_model short_name should be "my_skill"


  Scenario: Skill description must not be empty
    When I try to create a skill_model with empty description
    Then a skill_model validation error should be raised

    # ---- Include Resolution (flat) ----


  Scenario: Resolve skill with flat includes
    Given a skill_resolver registry with "local/basic-tools" containing refs "local/edit-file,local/read-file"
    And a skill_resolver root skill "local/code-tools" including "local/basic-tools" with own refs "local/format"
    When I resolve the skill_resolver root skill
    Then the skill_resolver result should have 3 tools
    And the skill_resolver result should contain tool "local/edit-file"
    And the skill_resolver result should contain tool "local/read-file"
    And the skill_resolver result should contain tool "local/format"

    # ---- Include Resolution (nested) ----


  Scenario: Resolve skill with nested includes
    Given a skill_resolver registry with "local/base" containing refs "local/core-tool"
    And a skill_resolver registry with "local/mid" including "local/base" and containing refs "local/mid-tool"
    And a skill_resolver root skill "local/top" including "local/mid" with own refs "local/top-tool"
    When I resolve the skill_resolver root skill
    Then the skill_resolver result should have 3 tools
    And the skill_resolver result should contain tool "local/core-tool"
    And the skill_resolver result should contain tool "local/mid-tool"
    And the skill_resolver result should contain tool "local/top-tool"

    # ---- Cycle Detection ----


  Scenario: Cycle detection with error message
    Given a skill_resolver registry with "local/a" including "local/b"
    And a skill_resolver registry with "local/b" including "local/a"
    When I try to resolve skill_resolver "local/a"
    Then a skill_resolver cycle error should be raised
    And the skill_resolver error should mention "local/a"
    And the skill_resolver error should mention "local/b"


  Scenario: Self-referencing cycle detection
    Given a skill_resolver registry with "local/self" including "local/self"
    When I try to resolve skill_resolver "local/self"
    Then a skill_resolver cycle error should be raised

    # ---- De-duplication ----


  Scenario: De-duplication behavior last wins for overrides
    Given a skill_resolver registry with "local/a" containing refs "local/shared-tool"
    And a skill_resolver registry with "local/b" containing refs "local/shared-tool"
    And a skill_resolver root skill "local/top" including "local/a,local/b" with no own refs
    When I resolve the skill_resolver root skill
    Then the skill_resolver result should have 1 tools
    And the skill_resolver tool "local/shared-tool" should come from "local/b"

    # ---- Override Application ----


  Scenario: Override application on tool refs
    When I create a skill_model with overrides for "local/edit-file"
    And I resolve the overridden skill_model
    Then the skill_resolver tool "local/edit-file" should have overrides

    # ---- Empty Skill ----


  Scenario: Empty skill with no tools or includes
    When I create a skill_model with name "local/empty" and description "Empty skill"
    And I resolve the empty skill_model
    Then the skill_resolver result should have 0 tools

    # ---- from_config Loading ----


  Scenario: Load skill from config dict
    When I load a skill_model from config with name "local/my-skill"
    Then the skill_model should be created
    And the skill_model name should be "local/my-skill"


  Scenario: Load skill from config missing name raises error
    When I try to load a skill_model from config missing "name"
    Then a skill_model config error should be raised with "name"


  Scenario: Load skill from config missing description raises error
    When I try to load a skill_model from config missing "description"
    Then a skill_model config error should be raised with "description"


  Scenario: Load skill from config with full YAML structure
    When I load a skill_model from full config
    Then the skill_model should be created
    And the skill_model should have 2 tool refs
    And the skill_model should have 1 include
    And the skill_model should have 1 anonymous tool
    And the skill_model should have 1 mcp server
    And the skill_model should have 1 agent skill


  Scenario: Load skill from config with string includes
    When I load a skill_model from config with string includes
    Then the skill_model should be created
    And the skill_model should have 1 include


  Scenario: Load skill from config with string agent skills
    When I load a skill_model from config with string agent skills
    Then the skill_model should be created
    And the skill_model should have 1 agent skill

    # ---- as_cli_dict Output ----


  Scenario: Skill as_cli_dict has required keys
    When I create a skill_model and call as_cli_dict
    Then the skill_model cli dict should have key "name"
    And the skill_model cli dict should have key "description"
    And the skill_model cli dict should have key "namespace"
    And the skill_model cli dict should have key "short_name"


  Scenario: Skill as_cli_dict includes optional sections when set
    When I create a skill_model with all fields and call as_cli_dict
    Then the skill_model cli dict should have key "tool_refs"
    And the skill_model cli dict should have key "includes"
    And the skill_model cli dict should have key "anonymous_tools"
    And the skill_model cli dict should have key "mcp_servers"
    And the skill_model cli dict should have key "agent_skills"
    And the skill_model cli dict should have key "overrides"


  Scenario: Skill as_cli_dict omits empty sections
    When I create a skill_model with name "local/minimal" and description "Minimal"
    And I get the skill_model cli dict
    Then the skill_model cli dict should not have key "tool_refs"
    And the skill_model cli dict should not have key "includes"
    And the skill_model cli dict should not have key "anonymous_tools"
    And the skill_model cli dict should not have key "mcp_servers"
    And the skill_model cli dict should not have key "agent_skills"
    And the skill_model cli dict should not have key "overrides"

    # ---- Inline Tool Validation ----


  Scenario: Inline tool with valid custom source
    When I create a skill_model inline tool with source "custom" and code "print('hi')"
    Then the skill_model inline tool should be created


  Scenario: Inline tool has default timeout of 300
    When I create a skill_model inline tool with source "custom" and code "print('hi')"
    Then the skill_model inline tool timeout should be 300


  Scenario: Inline tool timeout must be >= 1
    When I try to create a skill_model inline tool with timeout 0
    Then a skill_model validation error should be raised


  Scenario: Inline tool description must not be empty
    When I try to create a skill_model inline tool with empty description
    Then a skill_model validation error should be raised

    # ---- MCP Source Validation ----


  Scenario: MCP source with specific tools
    When I create a skill_model mcp source with server "npx @mcp/server" and tools "read,write"
    Then the skill_model mcp source should be created
    And the skill_model mcp source should have 2 tools


  Scenario: MCP source with all tools (none specified)
    When I create a skill_model mcp source with server "npx @mcp/server" and no tools
    Then the skill_model mcp source should be created
    And the skill_model mcp source tools should be none


  Scenario: MCP source with env vars
    When I create a skill_model mcp source with env vars
    Then the skill_model mcp source should be created


  Scenario: MCP source server must not be empty
    When I try to create a skill_model mcp source with empty server
    Then a skill_model validation error should be raised

    # ---- Agent Skill Source Validation ----


  Scenario: Agent skill source with valid path
    When I create a skill_model agent source with path "./skills/review"
    Then the skill_model agent source should be created


  Scenario: Agent skill source path must not be empty
    When I try to create a skill_model agent source with empty path
    Then a skill_model validation error should be raised

    # ---- CapabilitySummary Computation ----


  Scenario: CapabilitySummary computation for skill with inline tools
    When I compute skill_model capability summary for a skill with inline tools
    Then the skill_model summary total tools should be 4
    And the skill_model summary read_only_tools should be 1
    And the skill_model summary write_tools should be 1
    And the skill_model summary has_side_effects should be true
    And the skill_model summary mcp_sources should be 1
    And the skill_model summary agent_skill_sources should be 1


  Scenario: CapabilitySummary for empty skill
    When I compute skill_model capability summary for an empty skill
    Then the skill_model summary total tools should be 0

    # ---- SkillToolRef ----


  Scenario: SkillToolRef creation
    When I create a skill_model tool ref with name "local/my-tool"
    Then the skill_model tool ref should be created

    # ---- SkillInclude ----


  Scenario: SkillInclude creation with overrides
    When I create a skill_model include with name "local/base" and overrides
    Then the skill_model include should be created
    And the skill_model include should have overrides


  Scenario: SkillInclude creation without overrides
    When I create a skill_model include with name "local/base" without overrides
    Then the skill_model include should be created

    # ---- Missing included skill ----


  Scenario: Resolve with missing included skill raises error
    Given a skill_resolver root skill "local/broken" including "local/nonexistent" with no own refs
    When I try to resolve skill_resolver "local/broken"
    Then a skill_resolver missing skill error should be raised
    And the skill_resolver error should mention "local/nonexistent"

    # ---- MCP and agent skill resolution ----


  Scenario: Resolve skill with MCP and agent skill sources
    When I create and resolve a skill_model with mcp and agent sources
    Then the skill_resolver result should have 3 tools

    # ---- ResolvedToolEntry ----


  Scenario: ResolvedToolEntry creation
    When I create a skill_resolver resolved tool entry
    Then the skill_resolver entry should be created

    # ---- Robot Smoke Mirrors (Behave equivalents of robot/skill_resolution.robot) ----


  Scenario: Resolver output ordering matches depth-first include order
    Given a skill_resolver registry with "local/base-tools" containing refs "local/edit-file,local/read-file"
    And a skill_resolver root skill "local/code-tools" including "local/base-tools" with own refs "local/format"
    When I resolve the skill_resolver root skill
    Then the skill_resolver result should have 3 tools
    And the skill_resolver tool at index 0 should be "local/edit-file"
    And the skill_resolver tool at index 1 should be "local/read-file"
    And the skill_resolver tool at index 2 should be "local/format"


  Scenario: Resolver de-duplicates overlapping include tools with last-wins
    Given a skill_resolver registry with "local/a" containing refs "local/shared-tool,local/a-only"
    And a skill_resolver registry with "local/b" containing refs "local/shared-tool,local/b-only"
    And a skill_resolver root skill "local/top" including "local/a,local/b" with no own refs
    When I resolve the skill_resolver root skill
    Then the skill_resolver result should have 3 tools
    And the skill_resolver tool "local/shared-tool" should come from "local/b"


  Scenario: Resolver includes inline tools with correct anon keys
    When I create and resolve a skill_model with inline tools
    Then the skill_resolver result should contain tool "local/inline-test/_anon_0"
    And the skill_resolver result should contain tool "local/inline-test/_anon_1"
    And the skill_resolver inline tool at index 1 should be marked inline
