Feature: Skill Registry Refresh and MCP Notification Hooks
  As a CleverAgents runtime operator
  I want the SkillRegistry to refresh tool sets on demand and in response to
  MCP notifications/tools/list_changed events
  So that skills always reflect the current state of connected MCP servers

  # -------------------------------------------------------------------
  # SkillRefreshResult
  # -------------------------------------------------------------------

  Scenario: Refresh result summary contains counts for refreshed skills
    Given a skill refresh result with 3 refreshed, 1 failed, 2 skipped
    Then the refresh result total_refreshed should be 3
    And the refresh result total_failed should be 1
    And the refresh result total_skipped should be 2
    And the refresh result summary should be "refreshed: 3, failed: 1, skipped: 2"

  Scenario: Refresh result for all-success is correct
    Given a skill refresh result with 5 refreshed, 0 failed, 0 skipped
    Then the refresh result total_refreshed should be 5
    And the refresh result total_failed should be 0
    And the refresh result total_skipped should be 0
    And the refresh result summary should be "refreshed: 5, failed: 0, skipped: 0"

  Scenario: Refresh result for all-skipped is correct
    Given a skill refresh result with 0 refreshed, 0 failed, 3 skipped
    Then the refresh result total_skipped should be 3
    And the refresh result summary should be "refreshed: 0, failed: 0, skipped: 3"

  # -------------------------------------------------------------------
  # SkillRegistry.refresh(name) — happy path
  # -------------------------------------------------------------------

  Scenario: Refresh a single skill recomputes its tool set
    Given a skill registry without a tool registry
    And a registered skill "local/my-skill" with tool refs "local/tool-a,local/tool-b"
    When I refresh skill "local/my-skill"
    Then the refresh result total_refreshed should be 1
    And the refresh result total_failed should be 0
    And the refresh result total_skipped should be 0

  Scenario: Refresh a skill with a configured tool registry validates tool refs
    Given a skill registry with a tool registry containing "local/tool-a"
    And a registered skill "local/my-skill" with tool refs "local/tool-a"
    When I refresh skill "local/my-skill"
    Then the refresh result total_refreshed should be 1
    And the refresh result total_failed should be 0

  Scenario: Refresh a skill whose tool ref is missing from tool registry returns failure
    Given a skill registry with a tool registry containing "local/tool-a"
    And a registered skill "local/bad-skill" with tool refs "local/missing-tool"
    When I refresh skill "local/bad-skill"
    Then the refresh result total_failed should be 1
    And the refresh result failed skill "local/bad-skill" error mentions "local/missing-tool"

  # -------------------------------------------------------------------
  # SkillRegistry.refresh(name) — safeguard: no tool registry
  # -------------------------------------------------------------------

  Scenario: Refresh skips validation when tool registry is unavailable
    Given a skill registry without a tool registry
    And a registered skill "local/unvalidated-skill" with tool refs "local/any-tool"
    When I refresh skill "local/unvalidated-skill"
    Then the refresh result total_skipped should be 0
    And the refresh result total_refreshed should be 1

  Scenario: Refresh emits a warning when tool registry is unavailable
    Given a skill registry without a tool registry
    And a registered skill "local/warn-skill" with tool refs "local/some-tool"
    When I refresh skill "local/warn-skill" and capture log warnings
    Then no "tool registry" warning is logged during refresh without tool registry

  # -------------------------------------------------------------------
  # SkillRegistry.refresh(name) — error cases
  # -------------------------------------------------------------------

  Scenario: Refresh a non-existent skill raises SkillExecutionError
    Given an empty skill registry without a tool registry
    When I try to refresh non-existent skill "local/ghost-skill"
    Then a SkillExecutionError is raised mentioning "local/ghost-skill"

  # -------------------------------------------------------------------
  # SkillRegistry.refresh_all()
  # -------------------------------------------------------------------

  Scenario: refresh_all refreshes all registered skills
    Given a skill registry without a tool registry
    And a registered skill "local/skill-a" with tool refs "local/tool-x"
    And a registered skill "local/skill-b" with tool refs "local/tool-y"
    When I call refresh_all on the skill registry
    Then the refresh result total_refreshed should be 2
    And the refresh result total_failed should be 0

  Scenario: refresh_all with tool registry validates all skills
    Given a skill registry with a tool registry containing "local/tool-x"
    And a registered skill "local/good-skill" with tool refs "local/tool-x"
    And a registered skill "local/bad-skill" with tool refs "local/missing-tool"
    When I call refresh_all on the skill registry
    Then the refresh result total_refreshed should be 1
    And the refresh result total_failed should be 1

  Scenario: refresh_all on empty registry returns zero counts
    Given an empty skill registry without a tool registry
    When I call refresh_all on the skill registry
    Then the refresh result total_refreshed should be 0
    And the refresh result total_failed should be 0
    And the refresh result total_skipped should be 0

  # -------------------------------------------------------------------
  # MCPRefreshHook — notification wiring
  # -------------------------------------------------------------------

  Scenario: MCPToolAdapter dispatches registered notification listeners
    Given a fresh MCP adapter for notification testing
    And a notification listener registered on the adapter
    When the adapter dispatches "notifications/tools/list_changed" notification
    Then the notification listener should have been called with method "notifications/tools/list_changed"

  Scenario: MCPToolAdapter ignores unknown notification methods
    Given a fresh MCP adapter for notification testing
    And a notification listener registered on the adapter
    When the adapter dispatches "notifications/unknown" notification
    Then the notification listener should have been called with method "notifications/unknown"

  Scenario: MCPRefreshHook wires adapter notifications to skill registry refresh_all
    Given a fresh MCP adapter for notification testing
    And a skill registry without a tool registry
    And a registered skill "local/wired-skill" with tool refs "local/tool-z"
    And an MCPRefreshHook connecting the adapter to the skill registry with debounce 0.0
    When the adapter dispatches "notifications/tools/list_changed" notification
    And I wait for 0.1 seconds for debounce to fire
    Then the skill registry refresh_all should have been triggered

  Scenario: Multiple notifications within debounce window collapse to one refresh
    Given a fresh MCP adapter for notification testing
    And a skill registry without a tool registry
    And a registered skill "local/debounce-skill" with tool refs "local/tool-z"
    And an MCPRefreshHook connecting the adapter to the skill registry with debounce 2.0
    When the adapter dispatches "notifications/tools/list_changed" notification
    And the adapter dispatches "notifications/tools/list_changed" notification
    And the adapter dispatches "notifications/tools/list_changed" notification
    And I wait for 3.0 seconds for debounce to fire
    Then the skill registry refresh count should be 1

  Scenario: Non-tools-list-changed notifications are ignored by MCPRefreshHook
    Given a fresh MCP adapter for notification testing
    And a skill registry without a tool registry
    And an MCPRefreshHook connecting the adapter to the skill registry with debounce 0.0
    When the adapter dispatches "notifications/resources/list_changed" notification
    And I wait for 0.1 seconds for debounce to fire
    Then the skill registry refresh count should be 0

  Scenario: MCPRefreshHook cancel stops pending debounced refresh
    Given a fresh MCP adapter for notification testing
    And a skill registry without a tool registry
    And an MCPRefreshHook connecting the adapter to the skill registry with debounce 0.3
    When the adapter dispatches "notifications/tools/list_changed" notification
    And I cancel the MCPRefreshHook immediately
    And I wait for 0.5 seconds for debounce to fire
    Then the skill registry refresh count should be 0

  # -------------------------------------------------------------------
  # Refresh result summary for CLI output
  # -------------------------------------------------------------------

  Scenario: to_summary renders human-readable output for logging
    Given a skill refresh result with 2 refreshed, 1 failed, 0 skipped
    Then the refresh result summary should be "refreshed: 2, failed: 1, skipped: 0"
