Comprehensive PR-by-PR plan to realign Headroom around live-zone-only compression with prefix-cache safety as a non-negotiable invariant. Drafted from a 10-agent deep audit against the LLM-proxy compression guide. - 14 documents under REALIGNMENT/ - 72 ranked bugs (P0 cache-killers through P6 test-infra) - 40 feature PRs + 10 test-infra PRs across 9 phases - ~25K LOC retirement (ICM + scoring + relevance + rolling-window + summarizer + tool-crusher + LiteLLM-fake-Bedrock) - Preserves TOIN, CCR, Kompress-base per user direction - Auth-mode policy gates (PAYG / OAuth / subscription) - Phase 3 cache stabilization surface (tool-sort, schema-sort, cache_control auto-place, prompt_cache_key) - Native Bedrock SigV4 + Vertex ADC handlers - Test infrastructure: SHA-256 byte-faithful gate, SSE corner cases, property tests, real-traffic shadow
23 KiB
Phase B — Live-Zone-Only Compression Engine
Goal: Delete ~10 K LOC of architectural over-build (ICM, scoring, relevance, rolling-window, progressive-summarizer, tool-crusher); build the correct architecture: per-block compression on the live zone only, with type-aware dispatch, token validation, and CCR hardening.
Calendar: 2 weeks. PR-B1 is the big delete (high-LOC, lower-risk-than-it-looks because the code was unreachable after Phase A). PR-B2..B7 build the replacement.
Shape: 7 PRs. B1 is independent; B2..B5 depend on B1; B6 + B7 layer on B2.
PR-B1 — The big delete: retire ICM and its dependencies
Branch: realign-B1-delete-icm-and-deps
Worktree: ~/claude-projects/headroom-worktrees/realign-B1-delete-icm-and-deps
Risk: MEDIUM (large diff, but most code became unreachable after Phase A PR-A1)
LOC: -10,000 / +50 (the big retirement)
Scope
Delete the wrong-mental-model machinery wholesale. After Phase A PR-A1 made the proxy a passthrough on /v1/messages, none of this code is reached at runtime; this PR removes the source so future contributors can't re-wire it.
Files
Delete (Python):
headroom/transforms/intelligent_context.py(1077 LOC)headroom/transforms/rolling_window.py(395 LOC)headroom/transforms/progressive_summarizer.py(508 LOC)headroom/transforms/scoring.py(459 LOC)headroom/transforms/tool_crusher.py(338 LOC)
Delete (Rust):
crates/headroom-core/src/context/manager.rscrates/headroom-core/src/context/config.rscrates/headroom-core/src/context/workspace.rscrates/headroom-core/src/context/candidate.rscrates/headroom-core/src/context/ccr_drop.rscrates/headroom-core/src/context/strategy/mod.rscrates/headroom-core/src/context/strategy/drop_by_score.rs- All of
crates/headroom-core/src/scoring/*.rs(~1500 LOC) - All of
crates/headroom-core/src/relevance/*.rs(~1600 LOC) crates/headroom-core/.fastembed_cache/directory and itsbge-small-en-v1.5ONNX artifacts (~50 MB)
Move:
crates/headroom-core/src/context/safety.rs→crates/headroom-core/src/transforms/safety.rs. Update all callers'usepaths. The tool-pair atomicity logic is preserved verbatim (it's correct and live-zone code needs it).
Modify:
crates/headroom-core/src/lib.rs— removepub mod context;,pub mod scoring;,pub mod relevance;. Addpub use transforms::safety;.crates/headroom-core/src/context/mod.rs— delete (empty after move).crates/headroom-proxy/src/lib.rs— no changes (already doesn't reach into deleted modules after PR-A1).crates/headroom-py/src/lib.rs— remove any PyO3 exports ofMessageScorer,IntelligentContextManager, etc. (per agent reports, MessageScorer was exposed in PR #338/#343).headroom/transforms/__init__.py— remove imports of deleted modules.headroom/proxy/handlers/anthropic.py— remove all imports / call sites ofIntelligentContextManager. (Agent C found these at multiple locations; track viagrep -n IntelligentContextManager headroom/.)headroom/proxy/server.py— remove ICM import and instantiation.Cargo.tomlworkspace dependencies — dropfastembed,tantivy,ort(if only used by relevance), and any other deps that become orphaned.
Tests deleted:
- All
tests/test_intelligent_context*.py - All
tests/test_rolling_window*.py - All
tests/test_progressive_summarizer*.py - All
tests/test_scoring*.py - All
tests/test_tool_crusher*.py crates/headroom-core/tests/scoring_*.rs,relevance_*.rs,context_*.rs(other than safety)- Parity comparator for
message_scorer(PR #338/#343 work) — delete the comparator and the fixtures it consumed. - Parity fixtures
tests/parity/fixtures/message_scorer/(13 fixtures per Agent F report).
Acceptance criteria
cargo build --workspacegreen.cargo test --workspacegreen (after test deletions).make ci-precheckgreen.pytest -xgreen.- Workspace builds without the fastembed cache dir.
git grep -i "IntelligentContextManager\|MessageScorer\|RollingWindow\|ProgressiveSummarizer\|ToolCrusher\|DropByScoreStrategy"returns nothing incrates/,headroom/,tests/except comments referencing the deletion.
Blocked by
PR-A1 (ICM call site must be removed first).
Blocks
PR-B2 (live-zone block dispatcher fills the void).
Rollback
git revert. ~10K LOC returns. Cache-killer bugs DO NOT return because Phase A PR-A1 already removed the call site (the deleted code is unreachable). Safe to revert.
Notes
- MessageScorer Rust port retirement: PR #338 and #343 (April 2026) ported MessageScorer to Rust. That work becomes deletable here. Sunk cost stays sunk. The fixtures and the parity-harness scaffolding learnings carry forward to live-zone work.
bge-small-en-v1.5ONNX cache: ~50 MB. Removing it is reversible (re-fetched on next fastembed init if anyone re-adds the dep). Document in CHANGELOG.anchor_selector.py— Agent G suspected it might be ICM-only. Check:git grep AnchorSelector headroom/ crates/. If only consumed by ICM/scoring/SmartCrusher, delete; if consumed by SmartCrusher's anchor logic, keep. Current Rust hascrates/headroom-core/src/transforms/anchor_selector.rswhich is consumed by SmartCrusher — keep that one, delete the Python one if ICM was its only caller.
PR-B2 — Live-zone block dispatcher in Rust
Branch: realign-B2-live-zone-dispatcher
Worktree: ~/claude-projects/headroom-worktrees/realign-B2-live-zone-dispatcher
Risk: MEDIUM-HIGH (new architecture; the central piece)
LOC: +800
Scope
Build the new compressor: a function that takes an Anthropic /v1/messages body, identifies the live-zone blocks (latest user message tool_results, latest user message text, latest assistant tool_use is hot zone — exclude), and dispatches each to a type-aware compressor. Does NOT yet wire the type-aware compressors (PR-B3); does NOT yet validate tokens (PR-B4); does NOT yet inject CCR (PR-B7). PR-B2 lays the dispatching skeleton with no-op compressors; subsequent PRs fill them in.
Files
Add:
crates/headroom-core/src/transforms/live_zone.rs— the dispatcher. Public API:
Implementation skeleton:pub fn compress_live_zone( body_raw: &serde_json::value::RawValue, frozen_message_count: usize, auth_mode: AuthMode, ) -> Result<LiveZoneOutcome>; pub enum LiveZoneOutcome { NoChange, Modified { new_body: Box<serde_json::value::RawValue>, manifest: CompressionManifest }, }- Parse
bodyminimally (onlymessagesfield; leave the rest asRawValue). - For each message at index
>= frozen_message_count:- Identify if it's the latest user message (live zone candidate).
- For each block in its content:
- If block type is
tool_result, dispatch to a no-op compressor (filled in PR-B3). - If block type is
text, dispatch to text compressor (no-op for now). - Otherwise (image, etc.), no-op.
- If block type is
- Reassemble the modified
messagesarray, preserving unmodified messages asRawValuebyte-copies. - Reassemble the body, preserving the original envelope as
RawValuebyte-copies; the only modified bytes are within the messages array. - Return
Modifiedonly if any block was actually mutated; otherwiseNoChangeand the caller forwards original bytes.
- Parse
Modify:
crates/headroom-proxy/src/compression/mod.rs— addpub mod live_zone_anthropic;and route/v1/messagesto it (replacing the no-op stub from PR-A1).crates/headroom-proxy/src/compression/live_zone_anthropic.rs(new) — callscompress_live_zonewithfrozen_count = compute_frozen_count(parsed)(from PR-A4).crates/headroom-proxy/src/compression/anthropic.rs— delete (replaced bylive_zone_anthropic.rs).
Tests added:
crates/headroom-core/tests/live_zone_skeleton.rs::dispatches_only_to_latest_user_messagecrates/headroom-core/tests/live_zone_skeleton.rs::respects_frozen_message_countcrates/headroom-core/tests/live_zone_skeleton.rs::no_change_when_no_block_mutated_returns_originalcrates/headroom-core/tests/live_zone_skeleton.rs::modified_messages_byte_equal_outside_blockcrates/headroom-core/tests/live_zone_skeleton.rs::system_and_tools_byte_equal_alwayscrates/headroom-proxy/tests/integration_live_zone.rs::end_to_end_live_zone_passthrough
Acceptance criteria
cargo test -p headroom-coregreen.cargo test -p headroom-proxygreen.- All Phase A SHA-256 tests still pass (live-zone with no-op compressors is byte-identical).
Blocked by
PR-B1 (deletion); PR-A4 (cache_control / RawValue features).
Blocks
PR-B3, PR-B4, PR-B7.
Rollback
git revert. Compression returns to passthrough (the Phase A state); no functional regression.
Notes
- The
RawValue-based approach is the correctness mechanism: bytes outside modified blocks are byte-copies, not parse-then-reserialize. AuthModeparameter is unused in B2 (alwaysPaygfrom B2's perspective); Phase F PR-F2 wires the gate.
PR-B3 — Wire type-aware compressors into live-zone dispatcher
Branch: realign-B3-wire-type-aware-compressors
Worktree: ~/claude-projects/headroom-worktrees/realign-B3-wire-type-aware-compressors
Risk: MEDIUM (existing compressors are battle-tested)
LOC: +600
Scope
Wire SmartCrusher, LogCompressor, SearchCompressor, DiffCompressor, CodeCompressor into the dispatcher. Per-block content-type detection drives dispatch. No token validation yet (PR-B4); no CCR hardening yet (PR-B7).
Files
Modify:
crates/headroom-core/src/transforms/live_zone.rs— replace no-op compressors with real dispatch:fn compress_block(block: &mut Block, content_type: ContentType) -> Result<Option<CompressionResult>> { match content_type { ContentType::JsonArrayOfDicts => smart_crusher::crush(block), ContentType::Logs => log_compressor::compress(block), ContentType::SearchResults => search_compressor::compress(block), ContentType::Diff => diff_compressor::compress(block), ContentType::SourceCode => code_compressor::compress(block), ContentType::PlainText => Ok(None), // PR-B4 adds Kompress; for now, leave untouched ContentType::Image | ContentType::Unknown => Ok(None), } }crates/headroom-core/src/transforms/content_detector.rs— extendContentTypeenum with the variants above. Use existingMagika+unidiff-rs+ heuristic detectors.
Tests added:
crates/headroom-core/tests/live_zone_dispatch.rs::json_tool_result_routes_to_smart_crushercrates/headroom-core/tests/live_zone_dispatch.rs::log_tool_result_routes_to_log_compressorcrates/headroom-core/tests/live_zone_dispatch.rs::diff_tool_result_routes_to_diff_compressorcrates/headroom-core/tests/live_zone_dispatch.rs::source_code_tool_result_routes_to_code_compressorcrates/headroom-core/tests/live_zone_dispatch.rs::unknown_content_type_no_op
Acceptance criteria
- All new tests pass.
- Existing SmartCrusher / LogCompressor / DiffCompressor / SearchCompressor tests still pass (their code is unchanged; only the caller is new).
- A representative
/v1/messagesrequest with a 50KB JSON tool_result through the proxy results in measurable compression (>2× size reduction) and SHA-256-equal envelope outside the compressed block.
Blocked by
PR-B2.
Blocks
PR-B4 (token validation gate), PR-B7 (CCR injection).
Rollback
git revert. Live-zone goes back to no-op compressors.
PR-B4 — Token validation gate with fallback; per-content-type byte thresholds
Branch: realign-B4-token-validation-gate
Worktree: ~/claude-projects/headroom-worktrees/realign-B4-token-validation-gate
Risk: LOW
LOC: +250
Scope
Eliminate P3-33 and P3-34. After every per-block compression, run the tokenizer over original and compressed. If compressed.tokens >= original.tokens, fall back to original. Add per-content-type byte thresholds: code>2KB, JSON>1KB, logs>500B, plain text>5KB. Below threshold → no compression attempted.
Files
Modify:
crates/headroom-core/src/transforms/live_zone.rs— wrap each compressor call with:let original_tokens = tokenizer.count(&original_bytes)?; let compressed_tokens = tokenizer.count(&compressed_bytes)?; if compressed_tokens >= original_tokens { metrics::compression_rejected_by_token_check(compressor_name); return Ok(None); // fall back to original }crates/headroom-core/src/transforms/live_zone.rs::compress_block— gate on byte threshold per content type:const THRESHOLDS: &[(ContentType, usize)] = &[ (ContentType::SourceCode, 2048), (ContentType::JsonArrayOfDicts, 1024), (ContentType::Logs, 512), (ContentType::PlainText, 5120), (ContentType::Diff, 1024), (ContentType::SearchResults, 1024), ]; if block.bytes_len() < threshold_for(content_type) { return Ok(None); }
Tests added:
crates/headroom-core/tests/live_zone_thresholds.rs::below_threshold_no_compression_attemptedcrates/headroom-core/tests/live_zone_thresholds.rs::above_threshold_compression_attemptedcrates/headroom-core/tests/live_zone_token_validation.rs::compressed_more_tokens_falls_backcrates/headroom-core/tests/live_zone_token_validation.rs::compressed_fewer_tokens_accepted- Property test:
proptest! { fn live_zone_compression_token_count_non_increasing(blocks in arb_blocks_strategy()) { ... } }
Acceptance criteria
- All tests pass.
- A pathological input (already-minified JSON, dense base64) falls back to original instead of inflating tokens.
- Prometheus emits
compression_rejected_by_token_check_total{strategy=...}counter.
Blocked by
PR-B3.
Blocks
PR-B6, PR-B7.
Rollback
git revert. Token validation removed; bytes-only gate returns. Slight regression risk on pathological inputs.
PR-B5 — TOIN observation-only refactor
Branch: realign-B5-toin-observation-only
Worktree: ~/claude-projects/headroom-worktrees/realign-B5-toin-observation-only
Risk: MEDIUM (TOIN is a preserved primitive; refactor must maintain its learning value)
LOC: -300 / +400
Scope
Eliminate P2-27 and P5-56. Strip TOIN's request-time hint API; keep the recording API. Recommendations published between deploys via a CLI tool that aggregates and writes a TOML file the compressor loads at startup. Per-tenant aggregation key extended to (auth_mode, model_family, structure_hash).
Files
Modify:
headroom/telemetry/toin.py:853-927— removeget_recommendation()andCompressionHint. Replace with a no-op stub that returnsNone; deprecation warning in docstring.headroom/telemetry/toin.py:103—Patternaddsauth_mode: str,model_family: strfields.headroom/telemetry/toin.py:477, 496, 727, 729, 1248, 1256— change aggregation key fromsig_hashto(auth_mode, model_family, sig_hash)tuple. Update all dict-key uses.headroom/telemetry/toin.py:1596— keeptenant_prefixfor storage but document it's now redundant with the aggregation key.headroom/transforms/smart_crusher.py:446— remove theget_recommendation()call site. SmartCrusher is now deterministic; TOIN observes outcomes only.- New CLI:
headroom/cli/toin_publish.py— aggregates the on-disk TOIN store and producesrecommendations.toml. Run as part of the deploy pipeline. - New:
crates/headroom-core/src/transforms/recommendations.rs— loadsrecommendations.tomlat startup. Provides API likerecommendations::get(auth_mode, model, structure_hash) -> Option<Recommendation>. Used to bias which compressor variants to try first (deterministic; no per-request mutation).
Tests added:
tests/test_toin_observation_only.py::test_no_request_time_hint_api_exposedtests/test_toin_observation_only.py::test_aggregation_key_includes_auth_mode_and_modeltests/test_toin_observation_only.py::test_record_does_not_alter_compression_decisiontests/test_toin_publish.py::test_publish_command_writes_toml- Determinism property test:
proptest! { fn compressor_deterministic_under_toin(input in arb_input()) { let r1 = compress(input); let r2 = compress(input); assert_eq!(r1, r2); } }
Acceptance criteria
- All new tests pass.
- TOIN's
record_compressioncall sites still work (recording is kept). - Removing TOIN's recommendations.toml at startup makes compression behave as if TOIN had never observed anything (graceful degrade).
Blocked by
PR-B4.
Blocks
PR-F3 (auth-mode aggregation key requires the TOIN refactor).
Rollback
git revert. Per-request hint API returns; non-determinism returns.
Notes
- This PR preserves TOIN per user direction: the learning value is intact; the dangerous request-time mutation is gone.
PR-B6 — Memory subsystem refactor: live-zone tail injection only
Branch: realign-B6-memory-live-zone-tail
Worktree: ~/claude-projects/headroom-worktrees/realign-B6-memory-live-zone-tail
Risk: MEDIUM-HIGH (touches memory feature semantics)
LOC: -400 / +300
Scope
Eliminate P2-24. Memory retrieval moves out of the request lifecycle "auto-prepend" position. Two modes:
- Auto-tail mode (default for now): retrieval runs at request entry; results appended to the latest user message tail (live zone). Same content always positions at the same place. Deterministic results for the same query.
- Tool mode (preferred long-term): the model calls
memory_searchexplicitly; retrieval runs in the tool execution path, not in the prompt-construction path. Memory is opt-in, not invisible.
This PR ships auto-tail-mode as default; tool-mode is wired but off-by-default.
Files
Modify:
headroom/proxy/memory_handler.py:498-510— delete_inject_to_system_or_instructions. Replace with_append_to_latest_user_tail.headroom/proxy/handlers/openai.py:535-540— same.headroom/proxy/handlers/anthropic.py:1117-1135— promote the existing_append_context_to_latest_non_frozen_user_turnto be the default path.headroom/proxy/server.py:1050-1058— already deleted in PR-A2; verify nothing reintroduces it.headroom/proxy/memory_handler.py— addMemoryModeenum:AutoTail | Tool. DefaultAutoTail.Toolmode skips auto-injection entirely.
Tests added:
tests/test_memory_auto_tail.py::test_memory_appears_in_latest_user_message_tailtests/test_memory_auto_tail.py::test_memory_does_not_modify_system_or_toolstests/test_memory_auto_tail.py::test_same_query_byte_identical_across_runstests/test_memory_tool_mode.py::test_tool_mode_skips_auto_injection
Acceptance criteria
- All new tests pass.
- Existing memory feature tests pass (semantics preserved; position changes from system to user-tail).
- The bytes inserted are deterministic for the same query (no randomness in vector search results — verify or seed).
Blocked by
PR-A2, PR-B4.
Blocks
None (memory tool injection session-stickiness from PR-A7 stays).
Rollback
git revert. Memory returns to auto-prepend.
PR-B7 — CCR hardening: persistent backend + always-on tool registration
Branch: realign-B7-ccr-hardening
Worktree: ~/claude-projects/headroom-worktrees/realign-B7-ccr-hardening
Risk: MEDIUM
LOC: -150 / +600
Scope
Eliminate P2-25, P2-26. Two changes:
- Persistent CCR backend.
CcrStoretrait gets aSqliteCcrStoreimpl (default) and aRedisCcrStoreimpl (opt-in for multi-worker). The in-memory store stays for tests. RUST_DEV.md "Multi-worker deployment — CCR fragmentation" section gets updated. ccr_retrievetool always-on. Once a session has performed any CCR compression, the tool is registered inbody["tools"]for every subsequent request. The session ID derives from the existingsession_tracker_storeplumbing.
In Rust, the live-zone dispatcher writes <<ccr:HASH>> markers into the compressed block content (side-channel) and stores the original bytes in the configured backend.
Files
Add:
crates/headroom-core/src/ccr/backends/sqlite.rs— SQLite-backedCcrStore. Schema:ccr_entries(hash TEXT PRIMARY KEY, original BLOB, created_at INTEGER, ttl_seconds INTEGER). Auto-purge on read (WHERE created_at + ttl_seconds > now).crates/headroom-core/src/ccr/backends/redis.rs— Redis-backedCcrStore.SETEX hash ttl_seconds original.crates/headroom-core/src/ccr/backends/mod.rs—pub trait CcrStore(already exists atccr.rs);pub fn from_config(config: &CcrConfig) -> Box<dyn CcrStore>.
Modify:
crates/headroom-core/src/ccr.rs— extractInMemoryCcrStoreto its own file; rest stays.crates/headroom-core/src/transforms/live_zone.rs— when a compressor returns aCompressionResultwith original bytes, store original bytes in CCR backend keyed byBLAKE3(original_bytes). Append<<ccr:HASH>>marker to compressed block content.headroom/proxy/handlers/anthropic.py—inject_ccr_retrieve_tool: always add the tool whensession.has_done_ccris true; never toggle off.headroom/proxy/handlers/openai.py— same for OpenAI Chat / Responses.headroom/ccr/tool_injection.py:302-328— changeif has_compressed_content:toif session.has_done_ccr:.RUST_DEV.md— update "Multi-worker deployment — CCR fragmentation" section: withSqliteCcrStore+ sticky-session not required; withRedisCcrStoreno stickiness needed at all.
Tests added:
crates/headroom-core/tests/ccr_backends.rs::sqlite_round_tripcrates/headroom-core/tests/ccr_backends.rs::sqlite_ttl_purgecrates/headroom-core/tests/ccr_backends.rs::redis_round_trip(gated behindcfg(feature = "redis"))crates/headroom-core/tests/ccr_backends.rs::backend_swap_byte_equal_keystests/test_ccr_tool_always_on.py::test_tool_registered_on_every_request_after_first_ccrtests/test_ccr_tool_always_on.py::test_tool_not_registered_if_session_never_did_ccrtests/test_ccr_tool_always_on.py::test_tool_definition_byte_stable
Acceptance criteria
- All new tests pass.
RUST_DEV.mdreflects the new multi-worker story.- A simulated proxy restart (kill + restart with
SqliteCcrStore) can still resolve CCR markers from before the restart. - Tool definition bytes are byte-stable (snapshot test pins them).
Blocked by
PR-B2, PR-B3, PR-B4.
Blocks
None.
Rollback
git revert. In-memory-only CCR returns; tool-list flip returns. Operations stays — just less safe.
Notes
- The
<<ccr:HASH>>marker format is unchanged — existing markers from before this PR (in any cached prefix) still work. - The session ID for "has done CCR" is the existing
session_idfromsession_tracker_store; no new persistence needed.
Phase B acceptance summary
After all 7 PRs land:
- ✅ ICM + RollingWindow + ProgressiveSummarizer + scoring + relevance + ToolCrusher deleted (~10K LOC retired)
- ✅ Live-zone block dispatcher operational
- ✅ Type-aware compressors wired (SmartCrusher, LogCompressor, SearchCompressor, DiffCompressor, CodeCompressor)
- ✅ Token validation gate with per-type byte thresholds and fallback
- ✅ TOIN observation-only with per-tenant aggregation key
- ✅ Memory routes to live-zone tail (no system mutation)
- ✅ CCR persistent backend + always-on tool registration
- ✅ MessageScorer Rust port (PR #338, #343) retired
Phase B retires P0-4, P1-13, P2-18 through P2-27, P3-33, P3-34, P5-56, P6-70.
After Phase B, Headroom's compression value is back online — and now it's correct.