headroom/tests/test_ccr.py
Tejas Chopra b7be3814f1
feat: compression extraction — Rust knob exposure, CCR hardening, traffic audits (#818)
## Description

A data-driven push for better compression savings without accuracy loss,
in four parts: expose and tune the Rust compressor knobs, harden the CCR
retrieval store, add traffic-audit tooling that sizes opportunities from
real transcripts, and introduce **read maturation** — a new,
live-validated mechanism that compresses Read outputs *before* they ever
enter the provider prefix cache.

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [x] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [x] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

### 1. Rust compressor extraction

- Expose `lossless_min_savings_ratio` end-to-end and lower the default
0.30 → 0.15 (lockstep across Rust, PyO3, and both Python config classes)
so the lossless Table/CSV compaction path wins more often.
- Expose the `CompactConfig` heuristics (core-field fraction,
heterogeneity ratio, flatten cap, bucket bounds) through PyO3 + Python.
- `SearchCompressor` grouped-by-file output (`rg --heading` style — path
once per file instead of per match). Library default off; the proxy
enables it in token mode.
- Complete `factor_out_constants`: constant fields now emit once in a
`_constant_fields` sentinel with slim rows (defensive per-item value
match; default off).
- `ContentRouter` accepts a SmartCrusher config override and the
search-grouping knob.

### 2. CCR store hardening

- Session-scale TTL: 300s → 1800s (CCRConfig, CompressionEntry,
CompressionStore, Rust `DEFAULT_TTL` — lockstep).
- **SQLite is the default CCR backend** (`~/.headroom/ccr_store.db`,
WAL): survives proxy restarts and is shared across workers.
`HEADROOM_CCR_BACKEND=memory` opts out.
- Multi-worker safety: `busy_timeout`, and corruption detection narrowed
so transient `SQLITE_BUSY` errors can never trigger database deletion.
- Data-at-rest hygiene: `chmod 600` on db + sidecars, expired rows swept
at open.
- Retrieval-miss messages are actionable (re-read the file / re-run the
command).

### 3. Traffic audit tooling (measure before tuning)

- `headroom audit-reads`: sizes Read opportunities from local Claude
Code transcripts (read share, stale %, line-number overhead, context
residency, cache-death windows).
- `--simulate-maturation`: Mechanism B risk sizing (re-read rates,
never-touched-again share, quiesce coverage, at-risk edits).
- `--codex`: shell-read classifier for Codex transcripts (rtk-wrapper
aware, workdir resolution).
- Findings that shaped this PR (81 sessions): Reads are 67% of tool
bytes; median Read lingers 118 turns (~13x lifetime cost); a prototyped
repeat-Read dedup measured 0.1% and was **removed** rather than shipped
as dead code.

### 4. Read maturation (Mechanism B) — experimental, default OFF

- Activity-based: a fresh large Read is held **out** of the provider
cache (trailing breakpoint relocated before it), stays verbatim while
its file is active, and matures into a CCR-backed marker once the file
is quiet for `quiesce_turns` (default 5; `max_hold_turns` bounds busy
files).
- Only the final compressed form ever enters the cache — **no cached
byte is ever mutated**; matured markers replay byte-identically.
- Wired into the Anthropic handler behind `--read-maturation` /
`HEADROOM_READ_MATURATION=1`; session state rides on the prefix tracker;
advisory (can never fail a request).
- Live-validated against the Anthropic API: held content excluded from
cache_creation; after maturation the prior cached prefix still served —
the no-bust invariant holds end-to-end.

### 5. Rebase / CI fixups (this update)

- Rebased onto latest `main` (was 28 commits behind): picks up `ci: pass
CODECOV_TOKEN to coverage uploads (#968)`, which is what was turning the
4 test shards red — the tests themselves passed (1528) but the post-test
codecov upload exited non-zero on a protected branch.
- Resolved the duplicate `lossless_min_savings_ratio` that two
independent main/branch additions left in `SmartCrusherConfig` and the
Rust-config kwarg (import-time `SyntaxError` + mypy `no-redef`).
- Aligned CCR tests with the new defaults (SQLite backend, 1800s TTL)
across `test_ccr`, `test_adapter_hooks`, `test_compression_store`,
`test_proxy_ccr`, and the lossy row-drop bridge test.

## Testing

<!-- Check what you actually ran, then paste the real command output
below. -->

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [ ] Manual testing performed

### Test Output

```text
$ python -m pytest tests/test_proxy_ccr.py tests/test_ccr.py tests/test_compression_store.py tests/test_adapter_hooks.py tests/test_ccr_row_drop_store_bridge.py -q
170 passed, 4 warnings in 42.49s

$ python -m pytest tests/test_audit_reads.py tests/test_audit_codex.py tests/test_read_maturation.py tests/test_transforms_content_router.py tests/test_smart_crusher_toin_attachment.py -q
83 passed

$ mypy headroom/
Success: no issues found in 365 source files

$ python -m compileall headroom/ -q
COMPILE-OK

# CI (run 27488990477, pre-rebase head): all 4 shards ran to completion —
#   "1528 passed, 120 skipped, 4922 deselected"
# The red shards were the codecov upload step, not test failures; fixed by
# the #968 rebase above.
```

## Real Behavior Proof

- Environment: macOS (darwin), Python 3.12 venv; branch
`feat/compression-extraction` rebased onto `origin/main` (head
7cb0f43b); GitHub Actions CI run 27488990477 for the test shards
- Exact command / steps: rebased onto latest main (clean, 13 commits
replayed, 0 conflicts); ran the pytest suites and mypy above locally;
inspected CI shard logs to confirm the failure was the codecov upload,
not the test phase
- Observed result: 253 targeted tests pass locally; mypy clean on 365
files; CI test phase reports `1528 passed, 120 skipped`; the only red
step (codecov `upload-coverage` → "Token required because branch is
protected") is resolved by the rebased-in #968 CODECOV_TOKEN fix
- Not tested: the read-maturation live-API no-bust validation
(`tests/test_live/`) was not re-run in this rebase pass (requires
provider keys); it was validated when the feature first landed, and no
maturation code changed in the rebase — only CCR-default test assertions
and the duplicate-field resolution

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Additional Notes

CHANGELOG is generated by release-please from the conventional commits,
so the CHANGELOG box is intentionally left unchecked. "Manual testing
performed" is unchecked deliberately — see `Real Behavior Proof` → `Not
tested` for the exact boundary (the live-API maturation validation was
not re-run in this rebase pass).

### Follow-ups (tracked, not in this PR)

- Mechanism B provider extensions: OpenAI-family wiring (no breakpoint
hold — bounded near-tail bust) and the Codex runtime read-detector (the
audit classifier is the prototype).
- Pilot enablement playbook: run `audit-reads --simulate-maturation` on
target traffic → pick `quiesce_turns` → enable via env → watch cache hit
rate + `read_maturation:N` transform tags.
2026-06-16 20:21:13 -07:00

445 lines
13 KiB
Python

"""Tests for Compress-Cache-Retrieve (CCR) architecture.
These tests verify that:
1. CompressionStore correctly caches compressed content
2. SmartCrusher integrates with CompressionStore
3. Retrieval works correctly (full and search)
4. Feedback tracking works
5. TTL expiration works
"""
import json
import time
import pytest
from headroom.cache.compression_store import (
CompressionStore,
get_compression_store,
reset_compression_store,
)
from headroom.config import CCRConfig
from headroom.transforms.smart_crusher import (
SmartCrusherConfig,
smart_crush_tool_output,
)
class TestCompressionStore:
"""Test CompressionStore functionality."""
@pytest.fixture(autouse=True)
def reset_store(self):
"""Reset global store before each test."""
reset_compression_store()
yield
reset_compression_store()
def test_store_and_retrieve(self):
"""Basic store and retrieve flow."""
store = CompressionStore()
original = json.dumps([{"id": i} for i in range(100)])
compressed = json.dumps([{"id": i} for i in range(10)])
hash_key = store.store(
original=original,
compressed=compressed,
original_tokens=1000,
compressed_tokens=100,
original_item_count=100,
compressed_item_count=10,
)
assert (
len(hash_key) == 24
) # SHA256 truncated to 24 chars (96 bits for collision resistance)
entry = store.retrieve(hash_key)
assert entry is not None
assert entry.original_content == original
assert entry.compressed_content == compressed
assert entry.original_tokens == 1000
assert entry.compressed_tokens == 100
def test_retrieve_nonexistent(self):
"""Retrieve returns None for nonexistent hash."""
store = CompressionStore()
entry = store.retrieve("nonexistent1234")
assert entry is None
def test_ttl_expiration(self):
"""Entries expire after TTL."""
store = CompressionStore(default_ttl=1) # 1 second TTL
hash_key = store.store(
original="[1,2,3]",
compressed="[1]",
ttl=1,
)
# Should exist immediately
assert store.exists(hash_key)
# Wait for expiration
time.sleep(1.1)
# Should be expired
assert not store.exists(hash_key)
entry = store.retrieve(hash_key)
assert entry is None
def test_eviction_at_capacity(self):
"""Oldest entries evicted when at capacity."""
store = CompressionStore(max_entries=3)
hashes = []
for i in range(5):
h = store.store(
original=f"original_{i}",
compressed=f"compressed_{i}",
)
hashes.append(h)
time.sleep(0.01) # Ensure different timestamps
# Only last 3 should exist
assert not store.exists(hashes[0])
assert not store.exists(hashes[1])
assert store.exists(hashes[2])
assert store.exists(hashes[3])
assert store.exists(hashes[4])
def test_search_with_bm25(self):
"""Search within cached content using BM25."""
store = CompressionStore()
items = [
{"id": 1, "content": "Python programming language"},
{"id": 2, "content": "JavaScript web development"},
{"id": 3, "content": "Python data science pandas"},
{"id": 4, "content": "Java enterprise applications"},
{"id": 5, "content": "Python machine learning tensorflow"},
]
hash_key = store.store(
original=json.dumps(items),
compressed=json.dumps(items[:2]),
original_item_count=5,
compressed_item_count=2,
)
# Search for Python items
results = store.search(hash_key, "Python programming")
assert len(results) >= 1
# Should prioritize Python items
result_ids = [r["id"] for r in results]
assert 1 in result_ids # "Python programming language"
def test_retrieval_tracking(self):
"""Retrieval events are tracked for feedback."""
store = CompressionStore(enable_feedback=True)
hash_key = store.store(
original="[1,2,3]",
compressed="[1]",
tool_name="test_tool",
)
# Retrieve multiple times
store.retrieve(hash_key)
store.retrieve(hash_key, query="test query")
store.search(hash_key, "another query")
events = store.get_retrieval_events(limit=10)
assert len(events) >= 2
# Check event details
assert any(e.retrieval_type == "full" for e in events)
assert any(e.retrieval_type == "search" for e in events)
def test_access_tracking_on_entry(self):
"""Entry tracks access count and queries."""
store = CompressionStore()
hash_key = store.store(
original=json.dumps([{"id": i} for i in range(10)]),
compressed="[]",
)
# Access multiple times with queries
store.retrieve(hash_key, query="first query")
store.retrieve(hash_key, query="second query")
store.retrieve(hash_key, query="first query") # Duplicate
entry = store.retrieve(hash_key)
assert entry.retrieval_count >= 3
assert "first query" in entry.search_queries
assert "second query" in entry.search_queries
def test_stats(self):
"""Store statistics are accurate."""
store = CompressionStore()
store.store(
original="x" * 100,
compressed="x" * 10,
original_tokens=100,
compressed_tokens=10,
)
store.store(
original="y" * 200,
compressed="y" * 20,
original_tokens=200,
compressed_tokens=20,
)
stats = store.get_stats()
assert stats["entry_count"] == 2
assert stats["total_original_tokens"] == 300
assert stats["total_compressed_tokens"] == 30
def test_global_store_singleton(self):
"""Global store uses singleton pattern."""
reset_compression_store()
store1 = get_compression_store()
store2 = get_compression_store()
assert store1 is store2
def test_thread_safety(self):
"""Store is thread-safe."""
import threading
store = CompressionStore()
hashes = []
lock = threading.Lock()
def store_item(i):
h = store.store(
original=f"original_{i}",
compressed=f"compressed_{i}",
)
with lock:
hashes.append(h)
threads = [threading.Thread(target=store_item, args=(i,)) for i in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
assert len(hashes) == 10
for h in hashes:
assert store.exists(h)
class TestCCRConfig:
"""Test CCR configuration options."""
def test_default_config(self):
"""Default CCR config values."""
config = CCRConfig()
assert config.enabled is True
assert config.store_max_entries == 1000
assert config.store_ttl_seconds == 1800 # session-scale (was 300)
assert config.inject_retrieval_marker is True
assert config.feedback_enabled is True
assert config.min_items_to_cache == 20
class TestCCRFeedbackLoop:
"""Test CCR feedback tracking for learning."""
@pytest.fixture(autouse=True)
def reset_store(self):
"""Reset global store before each test."""
reset_compression_store()
yield
reset_compression_store()
def test_retrieval_events_logged(self):
"""Retrieval events are logged for feedback."""
store = CompressionStore(enable_feedback=True)
items = [{"id": i, "data": f"item_{i}"} for i in range(50)]
hash_key = store.store(
original=json.dumps(items),
compressed=json.dumps(items[:10]),
original_item_count=50,
compressed_item_count=10,
tool_name="search_api",
)
# Simulate retrievals
store.retrieve(hash_key)
store.search(hash_key, "specific query")
store.search(hash_key, "another query")
events = store.get_retrieval_events(limit=10)
# Should have logged all retrievals
assert len(events) >= 3
# Check event types
full_events = [e for e in events if e.retrieval_type == "full"]
search_events = [e for e in events if e.retrieval_type == "search"]
assert len(full_events) >= 1
assert len(search_events) >= 2
def test_tool_name_in_events(self):
"""Tool name is preserved in retrieval events."""
store = CompressionStore(enable_feedback=True)
hash_key = store.store(
original="[1,2,3]",
compressed="[1]",
tool_name="github_search",
)
store.retrieve(hash_key)
events = store.get_retrieval_events(tool_name="github_search")
assert len(events) >= 1
assert all(e.tool_name == "github_search" for e in events)
def test_event_filtering_by_tool(self):
"""Events can be filtered by tool name."""
store = CompressionStore(enable_feedback=True)
hash1 = store.store(
original="[1]",
compressed="[1]",
tool_name="tool_a",
)
hash2 = store.store(
original="[2]",
compressed="[2]",
tool_name="tool_b",
)
store.retrieve(hash1)
store.retrieve(hash1)
store.retrieve(hash2)
tool_a_events = store.get_retrieval_events(tool_name="tool_a")
tool_b_events = store.get_retrieval_events(tool_name="tool_b")
assert len(tool_a_events) == 2
assert len(tool_b_events) == 1
class TestCCREdgeCases:
"""Test edge cases and error handling."""
@pytest.fixture(autouse=True)
def reset_store(self):
"""Reset global store before each test."""
reset_compression_store()
yield
reset_compression_store()
def test_search_expired_entry(self):
"""Search on expired entry returns empty."""
store = CompressionStore(default_ttl=1)
hash_key = store.store(
original=json.dumps([{"id": 1}]),
compressed="[]",
)
time.sleep(1.1)
results = store.search(hash_key, "query")
assert results == []
def test_search_invalid_json(self):
"""Search handles invalid JSON gracefully."""
store = CompressionStore()
hash_key = store.store(
original="not valid json",
compressed="[]",
)
results = store.search(hash_key, "query")
assert results == []
def test_search_non_array(self):
"""Search handles non-array content gracefully."""
store = CompressionStore()
hash_key = store.store(
original=json.dumps({"key": "value"}),
compressed="{}",
)
results = store.search(hash_key, "query")
assert results == []
def test_empty_query_search(self):
"""Search with empty query returns empty or all."""
store = CompressionStore()
items = [{"id": i} for i in range(10)]
hash_key = store.store(
original=json.dumps(items),
compressed="[]",
)
# Empty query should return something (BM25 handles this)
results = store.search(hash_key, "")
# Behavior depends on BM25 implementation
assert isinstance(results, list)
def test_ccr_disabled_no_caching(self):
"""When CCR disabled, no caching occurs."""
reset_compression_store()
items = [{"id": i, "score": 100 - i} for i in range(100)]
content = json.dumps(items)
config = SmartCrusherConfig(max_items_after_crush=15)
ccr_config = CCRConfig(enabled=False) # Disabled
smart_crush_tool_output(content, config, ccr_config)
store = get_compression_store()
stats = store.get_stats()
assert stats["entry_count"] == 0
def test_concurrent_store_and_retrieve(self):
"""Concurrent operations don't corrupt data."""
import threading
store = CompressionStore()
errors = []
def store_and_retrieve(i):
try:
items = [{"id": j, "batch": i} for j in range(10)]
hash_key = store.store(
original=json.dumps(items),
compressed="[]",
tool_name=f"tool_{i}",
)
# Immediately retrieve
entry = store.retrieve(hash_key)
if entry is None:
errors.append(f"Entry {i} not found after store")
elif f'"batch": {i}' not in entry.original_content:
errors.append(f"Entry {i} has wrong content")
except Exception as e:
errors.append(str(e))
threads = [threading.Thread(target=store_and_retrieve, args=(i,)) for i in range(20)]
for t in threads:
t.start()
for t in threads:
t.join()
assert errors == [], f"Errors during concurrent operations: {errors}"