fix(integrations): filter CCR-dropped sentinel in test iteration

The PR8 marker injection appends a sentinel object
{"_ccr_dropped": "<<ccr:HASH N_rows_offloaded>>"} to the kept-items
array on the lossy path so the LLM sees the retrieval pointer in the
prompt. Tests that iterate compressed arrays via subscript access
(e["level"], r["status"], i["labels"], m["text"]) hit KeyError on
the sentinel because it doesn't share the record schema.

Same root cause as the test_quality_retention fixes in PR8 -- these
integration tests were left out of that pass.

Ship a public helper headroom.transforms.smart_crusher.strip_ccr_sentinels
so tests can use it cleanly: `for e in strip_ccr_sentinels(entries):`
and production callers iterating compressed output get a single
canonical filter instead of inlining the _ccr_dropped check.

The 7 previously-failing tests in PR #292 CI now pass:
  - langchain test_100_percent_errors_preserved_logs
  - langchain test_errors_preserved_with_many_errors
  - langchain test_search_results_with_query_term
  - mcp test_all_log_errors_preserved
  - mcp test_slack_significant_compression_with_content
  - mcp test_database_error_status_preserved
  - mcp test_github_bugs_partial_preservation

753 tests across the integration + transforms + retention suites pass
locally. Plugin manifests auto-bumped 0.13.3 -> 0.13.4 by the
sync-plugin-versions hook (unrelated to this fix).
This commit is contained in:
chopratejas 2026-04-27 20:53:29 -07:00
parent da7716a95a
commit b8fc7eee19
7 changed files with 59 additions and 14 deletions

View file

@ -5,14 +5,14 @@
},
"metadata": {
"description": "Headroom marketplace for Claude Code and GitHub Copilot CLI plugins.",
"version": "0.13.2"
"version": "0.13.4"
},
"plugins": [
{
"name": "headroom",
"source": "./plugins/headroom-agent-hooks",
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
"version": "0.13.2",
"version": "0.13.4",
"author": {
"name": "Headroom Contributors",
"url": "https://github.com/chopratejas/headroom"

View file

@ -5,14 +5,14 @@
},
"metadata": {
"description": "Headroom marketplace for Claude Code and GitHub Copilot CLI plugins.",
"version": "0.13.2"
"version": "0.13.4"
},
"plugins": [
{
"name": "headroom",
"source": "./plugins/headroom-agent-hooks",
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
"version": "0.13.2",
"version": "0.13.4",
"author": {
"name": "Headroom Contributors",
"url": "https://github.com/chopratejas/headroom"

View file

@ -42,6 +42,39 @@ from .base import Transform
logger = logging.getLogger(__name__)
# ─── CCR sentinel ─────────────────────────────────────────────────────────
#
# When SmartCrusher's lossy path drops rows, it appends a sentinel object
# `{"_ccr_dropped": "<<ccr:HASH N_rows_offloaded>>"}` to the kept-items
# array. The LLM sees this in the prompt and can ask for the original via
# the CCR retrieval tool. Downstream consumers that iterate the array
# expecting a uniform schema (e.g. `for e in entries: e["level"]`) need
# to skip the sentinel — that's what `strip_ccr_sentinels` is for.
CCR_SENTINEL_KEY = "_ccr_dropped"
def is_ccr_sentinel(item: Any) -> bool:
"""True if `item` is a CCR-dropped sentinel object."""
return isinstance(item, dict) and CCR_SENTINEL_KEY in item
def strip_ccr_sentinels(items: Any) -> Any:
"""Return `items` with any CCR-dropped sentinel objects filtered out.
Pass this through any iteration over a compressed array's contents
when your code expects a uniform-schema list of records. The sentinel
carries a `<<ccr:HASH ...>>` marker for the LLM and shouldn't be
confused for a record it has only the `_ccr_dropped` key.
Non-list inputs pass through unchanged so callers can wrap whatever
`json.loads` returned without first checking the shape.
"""
if not isinstance(items, list):
return items
return [x for x in items if not is_ccr_sentinel(x)]
# ─── Public dataclasses ───────────────────────────────────────────────────

View file

@ -1,6 +1,6 @@
{
"name": "headroom",
"version": "0.13.2",
"version": "0.13.4",
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
"author": {
"name": "Headroom Contributors",

View file

@ -1,6 +1,6 @@
{
"name": "headroom",
"version": "0.13.2",
"version": "0.13.4",
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
"author": {
"name": "Headroom Contributors",

View file

@ -18,6 +18,7 @@ import pytest
from headroom.config import SmartCrusherConfig
from headroom.providers import OpenAIProvider
from headroom.transforms import SmartCrusher
from headroom.transforms.smart_crusher import strip_ccr_sentinels
# Test fixtures for realistic data
@ -190,8 +191,12 @@ class TestErrorPreservation:
json_match = re.search(r"(\{.*\})", compressed_output, re.DOTALL)
compressed_data = json.loads(json_match.group(1) if json_match else compressed_output)
# Count preserved errors
compressed_errors = [e for e in compressed_data["entries"] if e["level"] == "ERROR"]
# Count preserved errors. Strip CCR-dropped sentinel objects
# before iterating — they carry the retrieval marker for the LLM
# but don't share the entry schema.
compressed_errors = [
e for e in strip_ccr_sentinels(compressed_data["entries"]) if e["level"] == "ERROR"
]
# CRITICAL: 100% of errors must be preserved
assert len(compressed_errors) == len(original_errors), (
@ -226,7 +231,9 @@ class TestErrorPreservation:
json_match = re.search(r"(\{.*\})", compressed_output, re.DOTALL)
compressed_data = json.loads(json_match.group(1) if json_match else compressed_output)
compressed_errors = [e for e in compressed_data["entries"] if e["level"] == "ERROR"]
compressed_errors = [
e for e in strip_ccr_sentinels(compressed_data["entries"]) if e["level"] == "ERROR"
]
# Even with many errors, ALL must be preserved
assert len(compressed_errors) == len(original_errors), (
@ -314,7 +321,7 @@ class TestRelevancePreservation:
# At least some high-relevance results should be preserved
# (BM25 may not catch all without exact keyword matches)
compressed_high_relevance = [
r for r in compressed_data["results"] if r["relevance_score"] > 0.8
r for r in strip_ccr_sentinels(compressed_data["results"]) if r["relevance_score"] > 0.8
]
# With BM25, we should preserve at least 1 high-relevance result

View file

@ -19,6 +19,7 @@ from headroom.integrations.mcp import (
compress_tool_result_with_metrics,
)
from headroom.providers import OpenAIProvider
from headroom.transforms.smart_crusher import strip_ccr_sentinels
# ============================================================================
# Test Fixtures
@ -280,7 +281,9 @@ class TestMCPErrorPreservation:
compressed_data = json.loads(result.compressed_content)
compressed_errors = [
e for e in compressed_data["entries"] if e["level"] in ["ERROR", "FATAL"]
e
for e in strip_ccr_sentinels(compressed_data["entries"])
if e["level"] in ["ERROR", "FATAL"]
]
# CRITICAL: 100% of errors must be preserved
@ -306,7 +309,7 @@ class TestMCPErrorPreservation:
# Should preserve some messages with error keywords (SmartCrusher detects these)
error_msgs = [
m
for m in compressed_data["messages"]
for m in strip_ccr_sentinels(compressed_data["messages"])
if any(kw in m["text"].lower() for kw in ["error", "failed", "exception"])
]
assert len(error_msgs) > 0, "Should preserve some error messages"
@ -327,7 +330,9 @@ class TestMCPErrorPreservation:
compressed_data = json.loads(result.compressed_content)
compressed_errors = [
r for r in compressed_data["rows"] if "error" in str(r["status"]).lower()
r
for r in strip_ccr_sentinels(compressed_data["rows"])
if "error" in str(r["status"]).lower()
]
# Should preserve most error rows
@ -350,7 +355,7 @@ class TestMCPErrorPreservation:
# Should preserve at least some bugs
compressed_bugs = [
i
for i in compressed_data["issues"]
for i in strip_ccr_sentinels(compressed_data["issues"])
if any(label in ["bug", "critical", "urgent", "blocker"] for label in i["labels"])
]