test(compression): align no-op strike tests with structural backoff (#93022)

Two suites still encoded the pre-#93093 contract that the three
structural no-op branches (insufficient_messages, no_compressible_window,
empty_post_handoff_window) increment _ineffective_compression_count:

- tests/agent/test_compaction_anti_thrash.py::
  TestMinimumMessagesBranch::test_too_few_messages_records_an_ineffective_pass
- tests/run_agent/test_infinite_compaction_loop.py::
  TestCompressNoOpRegistersIneffective::{test_no_op_increments_counter,
  test_two_no_ops_block_should_compress}

Structural no-ops are transcript-shape facts, not evidence of an
incompressible floor, so they now arm _structural_no_op_backoff_until
and leave the strike counter untouched. Update the tests to pin the new
contract (count unchanged, backoff armed via time.monotonic(),
should_compress blocked while it holds) and rename accordingly. The
outcome contract of test_two_no_ops_block_should_compress is preserved:
repeated no-ops still block further automatic compression.
This commit is contained in:
aniruddhaadak80
2026-08-24 00:58:25 +05:30
committed by Teknium
parent f778c0d941
commit cd6c088928
2 changed files with 35 additions and 15 deletions

View File

@@ -30,6 +30,8 @@ Two subtleties this pins:
and disables compaction on a healthy session.
``test_no_false_positive_under_tokenizer_skew``.
"""
import time
import pytest
from agent.context_compressor import ContextCompressor
@@ -188,11 +190,13 @@ class TestFutilityGuard:
class TestMinimumMessagesBranch:
def test_too_few_messages_records_an_ineffective_pass(self):
"""Returning the transcript unchanged must move the anti-thrash state.
def test_too_few_messages_defers_via_structural_backoff(self):
"""A structurally impossible compaction must not strike the breaker.
Otherwise should_compress() keeps saying True about a transcript that can
never shrink, and every turn re-enters a no-op compaction.
#93022 — too-few-messages is a transcript-shape fact, not evidence
of an incompressible floor: striking it punished unrelated later
failures. Instead the branch arms the structural no-op backoff so
retries are deferred without burning anti-thrash strikes.
"""
cc = _compressor(threshold_tokens=1)
msgs = _messages(3, size=10)
@@ -202,7 +206,13 @@ class TestMinimumMessagesBranch:
assert len(out) == len(msgs), "nothing should have been compressed"
assert cc._last_compression_made_progress is False
assert cc._ineffective_compression_count == before + 1
assert cc._ineffective_compression_count == before, (
"structural no-op must leave the strike counter untouched"
)
assert cc._structural_no_op_backoff_until > time.monotonic(), (
"structural no-op must arm the retry backoff"
)
assert cc._compression_block_reason().startswith("structural_backoff")
class TestRejectedCompactionStrike:

View File

@@ -10,8 +10,9 @@ compressor to fire on every subsequent turn with no progress.
The fix adds two safeguards:
1. _find_tail_cut_by_tokens: when the whole transcript fits in soft_ceiling,
re-walk with the raw (non-inflated) budget to find a meaningful cut.
2. compress(): when compress_start >= compress_end, record the no-op as
an ineffective compression so should_compress() anti-thrashing fires.
2. compress(): when compress_start >= compress_end, defer retries via the
structural no-op backoff (#93022) so should_compress() anti-thrashing
fires without burning anti-thrash strikes on transcript-shape facts.
"""
from unittest.mock import patch, MagicMock
@@ -56,15 +57,16 @@ def _build_session(n_turns: int, words_per_turn: int = 20) -> list:
# ---------------------------------------------------------------------------
class TestCompressNoOpRegistersIneffective:
"""When compress_start >= compress_end, the fix records this as
an ineffective compression so the anti-thrashing guard fires.
"""When compress_start >= compress_end, the fix defers further attempts
via the structural no-op backoff (#93022) so the anti-thrashing guard
fires.
We trigger this path by having _find_tail_cut_by_tokens return
head_end (which makes compress_end = head_end + 1, same as
compress_start after alignment)."""
def test_no_op_increments_counter(self):
"""compress_start >= compress_end -> _ineffective_compression_count += 1"""
def test_no_op_arms_structural_backoff(self):
"""compress_start >= compress_end -> backoff armed, strikes untouched."""
comp = _make_compressor(
summary_target_ratio=0.45,
config_context_length=96000,
@@ -80,8 +82,14 @@ class TestCompressNoOpRegistersIneffective:
result = comp.compress(messages, current_tokens=73_000)
assert comp._ineffective_compression_count >= 1, (
f"Expected ineffective_compression_count >= 1, got {comp._ineffective_compression_count}"
assert len(result) == len(messages), (
"no-op compression must return the transcript unchanged"
)
assert comp._ineffective_compression_count == 0, (
"a structural impossibility is not an ineffective strike (#93022)"
)
assert comp._structural_no_op_backoff_until > time.monotonic(), (
"structural no-op must arm the retry backoff"
)
@@ -98,9 +106,11 @@ class TestCompressNoOpRegistersIneffective:
comp.compress(messages, current_tokens=73_000)
comp.compress(messages, current_tokens=73_000)
assert comp._ineffective_compression_count >= 2
assert comp._ineffective_compression_count == 0, (
"structural no-ops defer via backoff instead of striking (#93022)"
)
assert not comp.should_compress(73_000), (
"should_compress should return False after 2+ ineffective compressions"
"should_compress should return False while the structural backoff holds"
)