f778c0d941
Fixes #93022. A short session (protection window >= transcript) hits the "insufficient messages" / "no compressible window" branches twice and permanently trips the anti-thrash breaker, even though nothing was eligible to compress - compression was never attempted, so there is nothing "ineffective" to score. The session then rides past the threshold with no compaction possible (recovery probes only soften, not fix, the misclassification). Distinguish "nothing eligible right now" from "attempted and underperformed": - New transient _structural_no_op_backoff_until (in-memory, 300s) armed by _record_structural_no_op() at the three structural no-op sites: insufficient_messages, no_compressible_window, empty_post_handoff_window. No strikes accumulate; auto-compaction resumes on its own once the backoff lapses or the transcript outgrows the protection window. - The backoff gates should_compress via _automatic_compression_blocked_locally and surfaces in _compression_block_reason as "structural_backoff:<seconds>". - #40803's frozen-CLI guarantee is preserved: a transcript that can never shrink retries at most once per backoff window instead of every turn. - force=True (/compress) clears an active backoff before attempting; record_completed_compaction() lifts it - both prove the transcript is compressible/being worked. - Genuine attempted-but-underperformed verdicts still strike the durable ineffective counter unchanged. Tests: new tests/agent/test_context_compressor_structural_backoff.py; updated the two tests that asserted the old strike-on-noop behavior.