Feature: Skeleton compressor
  As an ACMS subsystem
  I want to compress context fragments for subplan inheritance
  So that child plans receive relevant context within a token budget

  Background:
    Given a skeleton compressor service

  # --- budget validation -------------------------------------------------

  Scenario: Reject negative skeleton_budget
    Given context fragments with total tokens 1000
    When I compress with skeleton_budget -1
    Then the compressor should raise a ValueError for invalid budget

  Scenario: Accept budget 0 returns empty
    Given context fragments with total tokens 1000
    When I compress with skeleton_budget 0
    Then the result should contain zero fragments

  Scenario: Accept large budget returns all fragments
    Given context fragments with total tokens 1000
    When I compress with skeleton_budget 1000
    Then all fragments should be returned unchanged

  Scenario: Accept budget at boundary 500
    Given context fragments with total tokens 1000
    When I compress with skeleton_budget 500
    Then compressed tokens should be at most 500

  # --- stable ordering --------------------------------------------------

  Scenario: Fragments with equal relevance are ordered by id
    Given three fragments with equal relevance 0.5
    When I compress with skeleton_budget 10000
    Then fragments should be ordered by fragment_id ascending

  Scenario: Fragments are ordered by relevance descending
    Given fragments with relevances 0.9, 0.3, and 0.7
    When I compress with skeleton_budget 10000
    Then the first fragment should have relevance 0.9
    And the last fragment should have relevance 0.3

  # --- edge cases -------------------------------------------------------

  Scenario: Empty fragment list compresses to empty
    Given an empty fragment list
    When I compress with skeleton_budget 500
    Then the result should contain zero fragments

  Scenario: Single fragment fits within budget
    Given a single fragment with 100 tokens
    When I compress with skeleton_budget 200
    Then the result should contain one fragment

  Scenario: Single fragment kept even when it exceeds budget
    Given a single fragment with 100 tokens
    When I compress with skeleton_budget 50
    Then the result should contain one fragment

  # --- argument validation ----------------------------------------------

  Scenario: Reject non-tuple fragments argument
    When I compress with a non-tuple fragments argument
    Then the compressor should raise a TypeError

  Scenario: Reject fragment with negative token count
    Given a fragment with negative token count
    When I compress with skeleton_budget 500
    Then the compressor should raise a ValueError for invalid fragment

  Scenario: Reject fragment with empty id
    Given a fragment with empty fragment_id
    When I compress with skeleton_budget 500
    Then the compressor should raise a ValueError for invalid fragment

  Scenario: Reject fragment with relevance out of range
    Given a fragment with relevance 1.5
    When I compress with skeleton_budget 500
    Then the compressor should raise a ValueError for invalid fragment

  Scenario: Reject non-ContextFragment item in tuple
    When I compress with a tuple containing a non-fragment item
    Then the compressor should raise a TypeError for invalid item

  Scenario: Reject non-integer skeleton_budget
    Given context fragments with total tokens 1000
    When I compress with a non-integer skeleton_budget
    Then the compressor should raise a TypeError for invalid budget type

  Scenario: Reject skeleton metadata with compressed exceeding original
    When I create skeleton metadata with compressed exceeding original
    Then a validation error should be raised for compressed exceeding original

  # --- skeleton_budget integration with plan model -----------------------

  Scenario: Plan model accepts skeleton_metadata
    Given a skeleton metadata with ratio 0.5 and 1000 original tokens and 500 compressed
    When I attach skeleton_metadata to a plan
    Then the plan should expose skeleton metadata in cli dict

  # --- _validate_fragments edge-cases ------------------------------------

  Scenario: Reject fragment with negative token_count
    Given a context fragment constructed with negative token_count
    When I validate the invalid fragments
    Then the compressor should raise a ValueError mentioning "token_count must be >= 0"

  Scenario: Reject fragment with out-of-range relevance_score
    Given a context fragment constructed with relevance_score 1.5
    When I validate the invalid fragments
    Then the compressor should raise a ValueError mentioning "relevance_score must be in"

  Scenario: Reject fragment with empty fragment_id
    Given a context fragment constructed with empty fragment_id
    When I validate the invalid fragments
    Then the compressor should raise a ValueError mentioning "fragment_id must be non-empty"

  # --- structural subtype assertion -------------------------------------

  Scenario: SkeletonCompressorService satisfies SkeletonCompressor protocol
    When I check that SkeletonCompressorService satisfies the SkeletonCompressor protocol
    Then the structural subtype assertion should pass
