@phase2 @acms @context_strategies @sliding_window
Feature: SlidingWindowStrategy with configurable window size
  As a CleverAgents developer
  I want a SlidingWindowStrategy that implements the ContextStrategy protocol
  So that long-running agent sessions can limit token usage by keeping only the most recent messages

  # ===========================================================================
  # Protocol conformance
  # ===========================================================================

  @sliding_window_protocol
  Scenario: SlidingWindowStrategy satisfies the ContextStrategy protocol
    Given a SlidingWindowStrategy with window_size 10 and window_mode "messages"
    Then the sw strategy should satisfy the ContextStrategy protocol
    And the sw strategy name should be "sliding_window"
    And the sw strategy should have a capabilities object

  @sliding_window_protocol
  Scenario: SlidingWindowStrategy explain returns a description
    Given a SlidingWindowStrategy with window_size 5 and window_mode "messages"
    Then the sw strategy explain should contain "sliding"

  # ===========================================================================
  # Messages mode — basic truncation
  # ===========================================================================

  @sliding_window_messages
  Scenario: Messages mode keeps only the most recent N messages
    Given a SlidingWindowStrategy with window_size 2 and window_mode "messages"
    And the following sliding window fragments:
      | uko_node              | content   | score | tokens | depth | role    |
      | project://app/a.py    | message 1 | 0.5   | 10     | 3     | user    |
      | project://app/b.py    | message 2 | 0.6   | 10     | 3     | user    |
      | project://app/c.py    | message 3 | 0.7   | 10     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 2 fragments should be returned by sliding window strategy
    And the sliding window result should contain uko_node "project://app/b.py"
    And the sliding window result should contain uko_node "project://app/c.py"
    And the sliding window result should not contain uko_node "project://app/a.py"

  @sliding_window_messages
  Scenario: Messages mode with exact boundary keeps all messages
    Given a SlidingWindowStrategy with window_size 3 and window_mode "messages"
    And the following sliding window fragments:
      | uko_node              | content   | score | tokens | depth | role    |
      | project://app/a.py    | message 1 | 0.5   | 10     | 3     | user    |
      | project://app/b.py    | message 2 | 0.6   | 10     | 3     | user    |
      | project://app/c.py    | message 3 | 0.7   | 10     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 3 fragments should be returned by sliding window strategy

  @sliding_window_messages
  Scenario: Messages mode with fewer messages than window size returns all
    Given a SlidingWindowStrategy with window_size 10 and window_mode "messages"
    And the following sliding window fragments:
      | uko_node              | content   | score | tokens | depth | role    |
      | project://app/a.py    | message 1 | 0.5   | 10     | 3     | user    |
      | project://app/b.py    | message 2 | 0.6   | 10     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 2 fragments should be returned by sliding window strategy

  @sliding_window_messages
  Scenario: Messages mode with empty context returns empty
    Given a SlidingWindowStrategy with window_size 5 and window_mode "messages"
    And an empty sliding window fragment list
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 0 fragments should be returned by sliding window strategy

  # ===========================================================================
  # Tokens mode — basic truncation
  # ===========================================================================

  @sliding_window_tokens
  Scenario: Tokens mode keeps most recent messages within token limit
    Given a SlidingWindowStrategy with window_size 25 and window_mode "tokens"
    And the following sliding window fragments:
      | uko_node              | content   | score | tokens | depth | role    |
      | project://app/a.py    | message 1 | 0.5   | 10     | 3     | user    |
      | project://app/b.py    | message 2 | 0.6   | 10     | 3     | user    |
      | project://app/c.py    | message 3 | 0.7   | 10     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 2 fragments should be returned by sliding window strategy
    And the sliding window result should contain uko_node "project://app/b.py"
    And the sliding window result should contain uko_node "project://app/c.py"
    And the sliding window result should not contain uko_node "project://app/a.py"

  @sliding_window_tokens
  Scenario: Tokens mode with exact boundary keeps all messages
    Given a SlidingWindowStrategy with window_size 30 and window_mode "tokens"
    And the following sliding window fragments:
      | uko_node              | content   | score | tokens | depth | role    |
      | project://app/a.py    | message 1 | 0.5   | 10     | 3     | user    |
      | project://app/b.py    | message 2 | 0.6   | 10     | 3     | user    |
      | project://app/c.py    | message 3 | 0.7   | 10     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 3 fragments should be returned by sliding window strategy

  @sliding_window_tokens
  Scenario: Tokens mode with empty context returns empty
    Given a SlidingWindowStrategy with window_size 100 and window_mode "tokens"
    And an empty sliding window fragment list
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 0 fragments should be returned by sliding window strategy

  @sliding_window_tokens
  Scenario: Tokens mode with single large message that fits returns it
    Given a SlidingWindowStrategy with window_size 50 and window_mode "tokens"
    And the following sliding window fragments:
      | uko_node              | content   | score | tokens | depth | role    |
      | project://app/a.py    | message 1 | 0.5   | 40     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 1 fragments should be returned by sliding window strategy

  # ===========================================================================
  # System prompt preservation
  # ===========================================================================

  @sliding_window_system_prompt
  Scenario: System prompt is always preserved outside the sliding window in messages mode
    Given a SlidingWindowStrategy with window_size 2 and window_mode "messages"
    And the following sliding window fragments:
      | uko_node                  | content        | score | tokens | depth | role    |
      | project://system/prompt   | system context | 0.9   | 20     | 5     | system  |
      | project://app/a.py        | message 1      | 0.5   | 10     | 3     | user    |
      | project://app/b.py        | message 2      | 0.6   | 10     | 3     | user    |
      | project://app/c.py        | message 3      | 0.7   | 10     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 3 fragments should be returned by sliding window strategy
    And the sliding window result should contain uko_node "project://system/prompt"
    And the sliding window result should contain uko_node "project://app/b.py"
    And the sliding window result should contain uko_node "project://app/c.py"
    And the sliding window result should not contain uko_node "project://app/a.py"

  @sliding_window_system_prompt
  Scenario: System prompt is always preserved outside the sliding window in tokens mode
    Given a SlidingWindowStrategy with window_size 25 and window_mode "tokens"
    And the following sliding window fragments:
      | uko_node                  | content        | score | tokens | depth | role    |
      | project://system/prompt   | system context | 0.9   | 20     | 5     | system  |
      | project://app/a.py        | message 1      | 0.5   | 10     | 3     | user    |
      | project://app/b.py        | message 2      | 0.6   | 10     | 3     | user    |
      | project://app/c.py        | message 3      | 0.7   | 10     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 3 fragments should be returned by sliding window strategy
    And the sliding window result should contain uko_node "project://system/prompt"
    And the sliding window result should contain uko_node "project://app/b.py"
    And the sliding window result should contain uko_node "project://app/c.py"
    And the sliding window result should not contain uko_node "project://app/a.py"

  @sliding_window_system_prompt
  Scenario: Multiple system prompts are all preserved
    Given a SlidingWindowStrategy with window_size 1 and window_mode "messages"
    And the following sliding window fragments:
      | uko_node                  | content          | score | tokens | depth | role    |
      | project://system/prompt1  | system context 1 | 0.9   | 20     | 5     | system  |
      | project://system/prompt2  | system context 2 | 0.8   | 20     | 5     | system  |
      | project://app/a.py        | message 1        | 0.5   | 10     | 3     | user    |
      | project://app/b.py        | message 2        | 0.6   | 10     | 3     | user    |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 3 fragments should be returned by sliding window strategy
    And the sliding window result should contain uko_node "project://system/prompt1"
    And the sliding window result should contain uko_node "project://system/prompt2"
    And the sliding window result should contain uko_node "project://app/b.py"
    And the sliding window result should not contain uko_node "project://app/a.py"

  @sliding_window_system_prompt
  Scenario: Context with only system prompts returns all of them
    Given a SlidingWindowStrategy with window_size 1 and window_mode "messages"
    And the following sliding window fragments:
      | uko_node                  | content          | score | tokens | depth | role    |
      | project://system/prompt1  | system context 1 | 0.9   | 20     | 5     | system  |
      | project://system/prompt2  | system context 2 | 0.8   | 20     | 5     | system  |
    And a sliding window budget with max_tokens 1000 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 2 fragments should be returned by sliding window strategy

  # ===========================================================================
  # Budget enforcement
  # ===========================================================================

  @sliding_window_budget
  Scenario: Strategy respects token budget after sliding window truncation
    Given a SlidingWindowStrategy with window_size 10 and window_mode "messages"
    And the following sliding window fragments:
      | uko_node              | content   | score | tokens | depth | role    |
      | project://app/a.py    | message 1 | 0.5   | 100    | 3     | user    |
      | project://app/b.py    | message 2 | 0.6   | 100    | 3     | user    |
      | project://app/c.py    | message 3 | 0.7   | 100    | 3     | user    |
    And a sliding window budget with max_tokens 250 and reserved_tokens 0
    When I apply the SlidingWindowStrategy
    Then 2 fragments should be returned by sliding window strategy

  # ===========================================================================
  # can_handle
  # ===========================================================================

  @sliding_window_can_handle
  Scenario: can_handle returns a positive confidence
    Given a SlidingWindowStrategy with window_size 10 and window_mode "messages"
    When I check can_handle on SlidingWindowStrategy
    Then the sliding window confidence should be greater than 0.0

  # ===========================================================================
  # Plugin registry registration
  # ===========================================================================

  @sliding_window_registry
  Scenario: SlidingWindowStrategy is registered under key "sliding_window" in the pipeline
    Given an ACMS pipeline for sliding window tests
    Then the sw pipeline should have strategy "sliding_window"

  @sliding_window_registry
  Scenario: SlidingWindowStrategy can be registered manually with the pipeline
    Given an ACMS pipeline for sliding window tests
    When I register a custom SlidingWindowStrategy with the pipeline
    Then the sw pipeline should have strategy "custom_sliding_window"

  # ===========================================================================
  # Configuration validation
  # ===========================================================================

  @sliding_window_config
  Scenario: window_size must be positive
    When I attempt to create a SlidingWindowStrategy with window_size 0
    Then a ValueError should be raised for sliding window

  @sliding_window_config
  Scenario: window_mode must be messages or tokens
    When I attempt to create a SlidingWindowStrategy with window_mode "invalid"
    Then a ValueError should be raised for sliding window

  @sliding_window_config
  Scenario: window_mode messages is valid
    When I create a SlidingWindowStrategy with window_size 5 and window_mode "messages"
    Then no error should be raised for sliding window

  @sliding_window_config
  Scenario: window_mode tokens is valid
    When I create a SlidingWindowStrategy with window_size 100 and window_mode "tokens"
    Then no error should be raised for sliding window
