@mock_only
Feature: Context tier runtime promotion, demotion, and eviction logic
  As an ACMS developer
  I want the ContextTierService to automatically manage fragment lifecycle
  So that frequently accessed fragments are promoted, stale fragments are
  demoted, and budget limits are enforced without manual intervention

  Background:
    Given a context tier service with default settings

  # -----------------------------------------------------------------
  # Auto-promotion on access
  # -----------------------------------------------------------------

  Scenario: Fragment promoted from cold to warm after reaching access threshold
    Given a fragment "frag-cold-01" stored in the cold tier with 50 tokens
    When I access "frag-cold-01" via get 5 times
    Then fragment "frag-cold-01" should be in the warm tier

  Scenario: Fragment promoted from warm to hot after reaching access threshold
    Given a fragment "frag-warm-01" stored in the warm tier with 50 tokens
    When I access "frag-warm-01" via get 5 times
    Then fragment "frag-warm-01" should be in the hot tier

  Scenario: Fragment not promoted before reaching access threshold
    Given a fragment "frag-cold-02" stored in the cold tier with 50 tokens
    When I access "frag-cold-02" via get 3 times
    Then fragment "frag-cold-02" should be in the cold tier

  Scenario: Promoted fragment requires fresh accesses for next promotion
    Given a fragment "frag-chain-01" stored in the cold tier with 50 tokens
    When I access "frag-chain-01" via get 5 times
    Then fragment "frag-chain-01" should be in the warm tier
    When I access "frag-chain-01" via get 1 times
    Then fragment "frag-chain-01" should be in the warm tier
    When I access "frag-chain-01" via get 5 times
    Then fragment "frag-chain-01" should be in the hot tier

  # -----------------------------------------------------------------
  # Staleness enforcement
  # -----------------------------------------------------------------

  Scenario: Stale hot fragment demoted to warm on staleness enforcement
    Given a fragment "frag-stale-hot" stored in the hot tier with stale timestamp of 25 hours
    When I invoke enforce_staleness on the service
    Then fragment "frag-stale-hot" should be in the warm tier

  Scenario: Fresh hot fragment not demoted on staleness enforcement
    Given a fragment "frag-fresh-hot" stored in the hot tier with fresh timestamp
    When I invoke enforce_staleness on the service
    Then fragment "frag-fresh-hot" should be in the hot tier

  Scenario: Stale warm fragment demoted to cold on staleness enforcement
    Given a fragment "frag-stale-warm" stored in the warm tier with stale timestamp of 25 hours
    When I invoke enforce_staleness on the service
    Then fragment "frag-stale-warm" should be in the cold tier

  Scenario: Staleness enforcement returns list of demoted fragment IDs
    Given a fragment "frag-stale-a" stored in the hot tier with stale timestamp of 25 hours
    And a fragment "frag-stale-b" stored in the hot tier with stale timestamp of 30 hours
    When I invoke enforce_staleness on the service
    Then the staleness result should contain 2 fragment IDs

  # -----------------------------------------------------------------
  # Budget enforcement on store
  # -----------------------------------------------------------------

  Scenario: Storing in hot tier when budget allows does not evict
    Given a service with hot tier budget of 200 tokens
    And a fragment "frag-a" stored in the hot tier with 50 tokens
    When I store fragment "frag-b" in the hot tier with 50 tokens
    Then fragment "frag-a" should be in the hot tier
    And fragment "frag-b" should be in the hot tier

  Scenario: Storing in hot tier over budget evicts oldest fragment
    Given a service with hot tier budget of 100 tokens
    And a fragment "frag-old" stored in the hot tier with 50 tokens and old timestamp
    And a fragment "frag-new" stored in the hot tier with 50 tokens and recent timestamp
    When I store fragment "frag-overflow" in the hot tier with 50 tokens
    Then fragment "frag-old" should not be in any tier
    And fragment "frag-new" should be in the hot tier
    And fragment "frag-overflow" should be in the hot tier

  Scenario: Oversized fragment redirected to warm tier instead of hot
    Given a service with hot tier budget of 100 tokens
    When I store fragment "frag-oversized" in the hot tier with 200 tokens
    Then fragment "frag-oversized" should be in the warm tier

  Scenario: Promotion to hot falls back to warm when budget evicts promoted fragment
    Given a context tier service with an event bus and hot budget of 100 tokens
    And a fragment "frag-resident" stored in the hot tier with 80 tokens and recent timestamp
    And a fragment "frag-to-promote" stored in the warm tier with 80 tokens and old timestamp
    When I promote fragment "frag-to-promote"
    Then fragment "frag-to-promote" should be in the warm tier
    And a TIER_PROMOTED event should have been emitted for "frag-to-promote"

  # -----------------------------------------------------------------
  # Event emission
  # -----------------------------------------------------------------

  Scenario: Promotion emits TIER_PROMOTED event with correct tier details
    Given a context tier service with an event bus
    And a fragment "frag-evt-01" stored in the cold tier with 50 tokens
    When I promote fragment "frag-evt-01"
    Then a TIER_PROMOTED event should have been emitted for "frag-evt-01"
    And the last TIER_PROMOTED event should have from_tier "cold" and to_tier "warm"

  Scenario: Demotion emits TIER_DEMOTED event with correct tier details
    Given a context tier service with an event bus
    And a fragment "frag-evt-02" stored in the hot tier with 50 tokens
    When I demote fragment "frag-evt-02"
    Then a TIER_DEMOTED event should have been emitted for "frag-evt-02"
    And the last TIER_DEMOTED event should have from_tier "hot" and to_tier "warm"

  Scenario: Budget eviction emits TIER_EVICTED event
    Given a context tier service with an event bus and hot budget of 100 tokens
    And a fragment "frag-evt-03" stored in the hot tier with 60 tokens and old timestamp
    When I store fragment "frag-evt-04" in the hot tier with 60 tokens
    Then a TIER_EVICTED event should have been emitted for "frag-evt-03"

  Scenario: Explicit evict_lru emits TIER_EVICTED events
    Given a context tier service with an event bus
    And a fragment "frag-evict-01" stored in the hot tier with 50 tokens and old timestamp
    And a fragment "frag-evict-02" stored in the hot tier with 50 tokens and recent timestamp
    When I evict 1 LRU fragment from the hot tier
    Then a TIER_EVICTED event should have been emitted for "frag-evict-01"

  Scenario: Oversized fragment redirect emits TIER_DEMOTED event
    Given a context tier service with an event bus and hot budget of 100 tokens
    When I store fragment "frag-oversized-evt" in the hot tier with 200 tokens
    Then fragment "frag-oversized-evt" should be in the warm tier
    And a TIER_DEMOTED event should have been emitted for "frag-oversized-evt"

  Scenario: Event emission failure does not break tier operation
    Given a context tier service with a failing event bus
    And a fragment "frag-evt-safe" stored in the cold tier with 50 tokens
    When I promote fragment "frag-evt-safe"
    Then fragment "frag-evt-safe" should be in the warm tier

  # -----------------------------------------------------------------
  # Edge cases
  # -----------------------------------------------------------------

  Scenario: Getting a nonexistent fragment returns nothing
    When I access "no-such-fragment" via get 1 times
    Then fragment "no-such-fragment" should not be in any tier

  Scenario: Promoting a hot-tier fragment has no effect
    Given a fragment "frag-already-hot" stored in the hot tier with 50 tokens
    When I promote fragment "frag-already-hot"
    Then fragment "frag-already-hot" should be in the hot tier

  Scenario: Demoting a cold-tier fragment has no effect
    Given a fragment "frag-already-cold" stored in the cold tier with 50 tokens
    When I demote fragment "frag-already-cold"
    Then fragment "frag-already-cold" should be in the cold tier

  Scenario: Staleness enforcement with mixed stale hot and warm fragments
    Given a context tier service with default settings
    And a fragment "mixed-hot" stored in the hot tier with stale timestamp of 25 hours
    And a fragment "mixed-warm" stored in the warm tier with stale timestamp of 25 hours
    When I invoke enforce_staleness on the service
    Then fragment "mixed-hot" should be in the warm tier
    And fragment "mixed-warm" should be in the cold tier
    And the staleness result should contain 2 fragment IDs

  Scenario: Demoted fragment requires fresh accesses before re-promotion
    Given a context tier service with default settings
    And a fragment "frag-pop" stored in the hot tier with 50 tokens
    When I access "frag-pop" via get 10 times
    And I invoke enforce_staleness on the service with a hot TTL of 0 hours
    And I access "frag-pop" via get 1 times
    Then fragment "frag-pop" should be in the warm tier

  Scenario: Promotion threshold of 1 promotes on first access without chain promotion
    Given a context tier service with promotion threshold 1
    And a fragment "frag-thresh" stored in the cold tier with 50 tokens
    When I access "frag-thresh" via get 1 times
    Then fragment "frag-thresh" should be in the warm tier
