Skip to content

fix(compressor): decay head protection across compression cycles#12092

Open
truenorth-lj wants to merge 1 commit into
NousResearch:mainfrom
truenorth-lj:fix/compressor-head-fossilization
Open

fix(compressor): decay head protection across compression cycles#12092
truenorth-lj wants to merge 1 commit into
NousResearch:mainfrom
truenorth-lj:fix/compressor-head-fossilization

Conversation

@truenorth-lj

Copy link
Copy Markdown
Contributor

Summary

  • Fix head message fossilization: after the first compression, decay protect_first_n to 1 (system message only) so old user/assistant messages become eligible for summarization instead of surviving every compression cycle indefinitely
  • System message (index 0) is always preserved regardless of compression count
  • Add 5 regression tests covering first compression, subsequent compressions, system message preservation, edge cases, and multi-cycle fossilization prevention

Changes

agent/context_compressor.py

  • In compress(), compute _effective_head based on compression_count — full protect_first_n on first compression, decays to 1 on subsequent compressions
  • Update _min_for_compress to use the effective head count

tests/agent/test_context_compressor.py

  • TestHeadProtectionDecay class with 5 tests:
    • test_first_compression_preserves_full_head — verifies original behavior on first compression
    • test_second_compression_decays_head_protection — verifies fossilized messages are included in compression window
    • test_system_message_always_preserved — verifies system prompt survives all compressions
    • test_protect_first_1_no_decay — edge case when protect_first_n=1
    • test_multiple_compressions_dont_fossilize — end-to-end 2-cycle simulation

Test plan

  • All 45 tests pass (40 existing + 5 new)
  • Manual verification: start a long Telegram session, trigger multiple compressions, confirm original user messages are summarized after the first compression

Closes #11996

🤖 Generated with Claude Code

After the first compression, reduce head protection from protect_first_n
to 1 (system message only). Previously, the first N messages were
preserved verbatim across every compression cycle, causing "fossilized"
messages that survived indefinitely even after being summarized.

The system message (index 0) is always preserved. User/assistant messages
in positions 1..protect_first_n are only protected during the first
compression — subsequent compressions include them in the summarization
window since their content is already captured in the summary.

Closes NousResearch#11996

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent loop, run_agent.py, prompt builder labels Apr 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Competing PR with #9199 — both address head fossilization in context_compressor.py. #9199 is the earlier attempt.

@truenorth-lj

Copy link
Copy Markdown
Contributor Author

Thanks for flagging @alt-glitch. After reading #9199, I think these are complementary rather than competing — they fix two different sub-cases of HEAD fossilization:

Concretely, even after #9199 lands, a long session with a system prompt and protect_first_n=3 will still re-inject the same first user/assistant pair into the HEAD on compressions 2, 3, 4, … — which is exactly what #11996 reports.

Happy to rebase on top of #9199 once it lands, or fold both fixes into a single PR if a maintainer prefers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: protect_first_n causes head message fossilization across compressions — old user messages become immortal

2 participants