Feature: Functional LSP Runtime
  As an actor runtime
  I need LSP servers to provide real code intelligence
  So that actors can get diagnostics and completions

  # ── StdioTransport ────────────────────────────────────────────────

  Scenario: StdioTransport rejects empty command
    When I try to create a StdioTransport with empty command
    Then a ValueError should be raised for transport

  Scenario: StdioTransport reports not alive before start
    Given I create a StdioTransport for "echo"
    Then the transport should not be alive

  Scenario: StdioTransport start and stop with echo
    Given I create a StdioTransport for "cat"
    When I start the transport
    Then the transport should be alive
    When I stop the transport
    Then the transport should not be alive

  # ── LspClient ─────────────────────────────────────────────────────

  Scenario: LspClient starts not initialized
    Given I create an LspClient with a mock transport
    Then the client should not be initialized

  Scenario: LspClient tracks request IDs
    Given I create an LspClient with a mock transport
    When I get two request IDs from the client
    Then the IDs should be sequential

  # ── LspLifecycleManager ──────────────────────────────────────────

  Scenario: LspLifecycleManager starts empty
    Given I create an LspLifecycleManager
    Then the manager should have no running servers

  Scenario: LspLifecycleManager stop unknown server raises error
    Given I create an LspLifecycleManager
    When I try to stop server "local/unknown" on the manager
    Then an LspServerNotFoundError should be raised for lifecycle

  Scenario: LspLifecycleManager health check returns false for unknown
    Given I create an LspLifecycleManager
    Then health check for "local/unknown" should return false

  # ── LspRuntime with registry ─────────────────────────────────────

  Scenario: LspRuntime start_server rejects empty name
    Given I create a functional LspRuntime
    When I try to start server with empty name and workspace "/tmp"
    Then a ValueError should be raised for runtime

  Scenario: LspRuntime start_server rejects empty workspace
    Given I create a functional LspRuntime
    When I try to start server "local/test" with empty workspace
    Then a ValueError should be raised for runtime

  Scenario: LspRuntime start_server raises for unregistered server
    Given I create a functional LspRuntime
    When I try to start unregistered server "local/missing"
    Then an LspServerNotFoundError should be raised for runtime

  Scenario: LspRuntime get_diagnostics rejects empty name
    Given I create a functional LspRuntime
    When I try to get diagnostics with empty name
    Then a ValueError should be raised for runtime

  Scenario: LspRuntime get_completions validates line >= 1
    Given I create a functional LspRuntime
    When I try to get completions with line 0
    Then a ValueError should be raised for runtime

  Scenario: LspRuntime get_completions validates column >= 1
    Given I create a functional LspRuntime
    When I try to get completions with column 0
    Then a ValueError should be raised for runtime

  # ── LanguageDiscovery ────────────────────────────────────────────

  Scenario: LanguageDiscovery detects Python from extension
    Given I create a LanguageDiscovery instance
    Then language for "test.py" should be "python"
    And language for "test.pyi" should be "python"

  Scenario: LanguageDiscovery detects TypeScript from extension
    Given I create a LanguageDiscovery instance
    Then language for "app.ts" should be "typescript"
    And language for "app.tsx" should be "typescriptreact"

  Scenario: LanguageDiscovery detects Rust from extension
    Given I create a LanguageDiscovery instance
    Then language for "main.rs" should be "rust"

  Scenario: LanguageDiscovery returns plaintext for unknown
    Given I create a LanguageDiscovery instance
    Then language for "data.xyz" should be "plaintext"

  Scenario: LanguageDiscovery detects Dockerfile by name
    Given I create a LanguageDiscovery instance
    Then language for "Dockerfile" should be "dockerfile"

  Scenario: LanguageDiscovery uses project language as fallback
    Given I create a LanguageDiscovery with project language "go"
    Then language for "unknown_file" should be "go"

  Scenario: LanguageDiscovery cache invalidation works
    Given I create a LanguageDiscovery instance
    When I detect language for "test.py"
    And I invalidate "test.py" from discovery cache
    Then the cache should not contain "test.py"

  Scenario: LanguageDiscovery detects shebang for Python script
    Given I create a LanguageDiscovery instance
    And I create a temp file with shebang "#!/usr/bin/env python3"
    Then the temp file language should be "python"

  Scenario: LanguageDiscovery detects shebang for bash script
    Given I create a LanguageDiscovery instance
    And I create a temp file with shebang "#!/bin/bash"
    Then the temp file language should be "shellscript"

  # ── LspToolAdapter with runtime ──────────────────────────────────

  Scenario: LspToolAdapter generates runtime-backed handlers when runtime provided
    Given I create an LspToolAdapter with a functional runtime
    And I have an LSP server config with diagnostics capability
    When I generate functional tool specs from the adapter
    Then the tool specs should have runtime handlers

  Scenario: LspToolAdapter generates local-mode handlers without runtime
    Given I create an LspToolAdapter without runtime
    And I have an LSP server config with diagnostics capability
    When I generate functional tool specs from the adapter
    Then the tool specs should have local-mode handlers

  # ── Binding activation ───────────────────────────────────────────

  Scenario: LspRuntime activate_bindings with no bindings returns empty
    Given I create a functional LspRuntime
    When I activate bindings with empty list
    Then the activated servers list should be empty

  Scenario: LspRuntime activate_bindings skips unregistered servers
    Given I create a functional LspRuntime
    When I activate bindings with unregistered server binding
    Then the activated servers list should be empty
