From 3f241e472b07c39efd8fd51e35b7070f5d514ea1 Mon Sep 17 00:00:00 2001 From: Vinay Gupta <58447456+aivinay@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:57:46 -0500 Subject: [PATCH] fix(ccr): skip compact summaries for proactive expansion (#2242) ## Description Fixes #2186. Claude Code `/compact` continuation summaries are already session context. When Headroom tracks those summaries for CCR proactive expansion, later fresh sessions can receive stale compacted history again inside `` blocks, increasing token usage and busting cache stability. This PR keeps CCR storage/retrieval intact but excludes probable Claude Code compact-summary payloads from the proactive-expansion tracker. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Added a narrow Claude Code compact-summary detector to the CCR context tracker. - Skipped tracking compact summaries when feeding Anthropic CCR metadata into proactive expansion. - Added an original-content preview to CCR metadata so the Anthropic feed point can classify compact summaries even when compressed text loses the distinctive header. - Added regression coverage proving compact summaries are not tracked and ordinary summaries are still eligible. ## Testing - [x] Unit tests pass - [x] Linting passes - [x] Formatting check passes - [x] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text $ pytest tests/test_ccr_context_tracker.py -q 37 passed $ uvx ruff==0.15.17 check headroom/ccr/context_tracker.py headroom/cache/compression_store.py headroom/proxy/handlers/anthropic.py tests/test_ccr_context_tracker.py --output-format concise All checks passed! $ uvx ruff==0.15.17 format --check headroom/ccr/context_tracker.py headroom/cache/compression_store.py headroom/proxy/handlers/anthropic.py tests/test_ccr_context_tracker.py 4 files already formatted ``` ## Real Behavior Proof - Environment: local checkout on macOS, Python test environment used by the repository. - Exact command / steps: ran the focused CCR context tracker suite after adding compact-summary detection and tracker-feed filtering. - Observed result: compact-summary payloads are not tracked for proactive expansion, ordinary summary-like tool output is still eligible, and the existing tracker behavior remains covered by the full focused suite. - Not tested: live Claude Code `/compact` session through a running proxy. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review - [x] I have added tests that prove the fix is effective - [ ] I have updated the CHANGELOG.md if applicable --- headroom/cache/compression_store.py | 1 + headroom/ccr/context_tracker.py | 39 ++++++++++++++++++++++++++++ headroom/proxy/handlers/anthropic.py | 11 ++++++++ tests/test_ccr_context_tracker.py | 32 +++++++++++++++++++++++ 4 files changed, 83 insertions(+) diff --git a/headroom/cache/compression_store.py b/headroom/cache/compression_store.py index f78fedcbb..b06108a58 100644 --- a/headroom/cache/compression_store.py +++ b/headroom/cache/compression_store.py @@ -480,6 +480,7 @@ class CompressionStore: "compressed_item_count": entry.compressed_item_count, "query_context": entry.query_context, "compressed_content": entry.compressed_content, + "original_content_preview": entry.original_content[:2000], "created_at": entry.created_at, "ttl": entry.ttl, } diff --git a/headroom/ccr/context_tracker.py b/headroom/ccr/context_tracker.py index aa98dcebe..885966407 100644 --- a/headroom/ccr/context_tracker.py +++ b/headroom/ccr/context_tracker.py @@ -31,6 +31,38 @@ from ..cache.compression_store import get_compression_store logger = logging.getLogger(__name__) +def looks_like_claude_code_compact_summary(*texts: str | None) -> bool: + """Return true for Claude Code `/compact` continuation summaries. + + Claude Code can carry a previous session forward by injecting a compact + conversation summary into a fresh session. Those summaries are already + context; tracking them for CCR proactive expansion makes Headroom re-add + stale session state to later turns. Keep this detector deliberately narrow + so ordinary tool output that happens to mention "summary" remains eligible. + """ + combined = " ".join(text.strip() for text in texts if text and text.strip()) + if not combined: + return False + + normalized = " ".join(combined.lower().split()) + has_summary = "summary" in normalized or "summarized" in normalized + + if "this session is being continued from a previous conversation" in normalized and has_summary: + return True + + if "conversation is summarized below" in normalized and ( + "ran out of context" in normalized or "previous conversation" in normalized + ): + return True + + return ( + "/compact" in normalized + and "claude" in normalized + and has_summary + and ("conversation" in normalized or "session" in normalized) + ) + + @dataclass class CompressedContext: """Represents a piece of compressed context from the conversation. @@ -157,6 +189,13 @@ class ContextTracker: if not self.config.enabled: return + if looks_like_claude_code_compact_summary(query_context, sample_content): + logger.debug( + "CCR Tracker: skipped Claude Code compact summary %s for proactive expansion", + hash_key, + ) + return + context = CompressedContext( hash_key=hash_key, turn_number=turn_number, diff --git a/headroom/proxy/handlers/anthropic.py b/headroom/proxy/handlers/anthropic.py index 427ee6413..bb9be767b 100644 --- a/headroom/proxy/handlers/anthropic.py +++ b/headroom/proxy/handlers/anthropic.py @@ -24,6 +24,7 @@ if TYPE_CHECKING: import httpx from headroom.agent_savings import proxy_pipeline_kwargs +from headroom.ccr.context_tracker import looks_like_claude_code_compact_summary from headroom.copilot_auth import build_copilot_upstream_url from headroom.pipeline import PipelineStage, summarize_routing_markers from headroom.proxy.auth_mode import classify_auth_mode, classify_client @@ -1934,6 +1935,16 @@ class AnthropicHandlerMixin: store = get_compression_store() entry = store.get_metadata(hash_key) if entry: + if looks_like_claude_code_compact_summary( + entry.get("query_context"), + entry.get("compressed_content"), + entry.get("original_content_preview"), + ): + logger.info( + f"[{request_id}] CCR: skipping proactive " + f"tracking for Claude Code compact summary {hash_key}" + ) + continue self.ccr_context_tracker.track_compression( hash_key=hash_key, turn_number=self._turn_counter, diff --git a/tests/test_ccr_context_tracker.py b/tests/test_ccr_context_tracker.py index ac8427cbc..fe45b3d69 100644 --- a/tests/test_ccr_context_tracker.py +++ b/tests/test_ccr_context_tracker.py @@ -23,6 +23,7 @@ from headroom.ccr.context_tracker import ( ContextTrackerConfig, ExpansionRecommendation, get_context_tracker, + looks_like_claude_code_compact_summary, reset_context_tracker, ) @@ -58,6 +59,37 @@ class TestContextTrackerBasics: stats = tracker.get_stats() assert stats["tracked_contexts"] == 1 + def test_claude_code_compact_summary_not_tracked(self): + """Claude Code `/compact` summaries are not proactively re-expanded.""" + tracker = ContextTracker() + + tracker.track_compression( + hash_key="compact_summary", + turn_number=1, + tool_name="Claude Code", + original_count=1, + compressed_count=1, + query_context="Claude Code /compact summary", + sample_content=( + "This session is being continued from a previous conversation " + "that ran out of context. The conversation is summarized below: " + "We changed the auth middleware and still need to run tests." + ), + workspace_key="ws-test", + ) + + assert tracker.get_tracked_hashes() == [] + + def test_claude_code_compact_summary_detector_is_narrow(self): + """The compact-summary detector does not reject ordinary summaries.""" + assert looks_like_claude_code_compact_summary( + "This session is being continued from a previous conversation. " + "The conversation is summarized below." + ) + assert not looks_like_claude_code_compact_summary( + "search results summary for auth middleware files" + ) + def test_track_multiple_compressions(self): """Track multiple compression events.""" tracker = ContextTracker()