headroom/tests/test_transforms
Nadia Ujovich 7c93c50c2c
Marker-free lossless_only mode + gate opaque-blob CCR markers behind enable_ccr_marker (#1129)
## Description

`enable_ccr_marker` only gated the **row-drop sentinel** path. The
**opaque-blob** path still emitted `<<ccr:HASH,kind,size>>` markers
unconditionally whenever a string cell exceeded `opaque_min_bytes`
(256), so **no configuration could produce a fully marker-free prompt**.
Any `<<ccr:>>` marker is a promise that the full payload lives in the
CCR store and must be fetched back via a retrieval tool call — there was
no way to get compression without that round-trip dependency.

**Rebased on #1130 (merged).** That PR fixed the opaque-blob gate at the
classifier (`ClassifyConfig.emit_opaque_markers`, driven by
`enable_ccr_marker`) and closed #1091. This branch originally carried
its own equivalent gating commit; that commit is now **redundant and has
been dropped** — `classifier.rs` here is identical to upstream. What
remains is the **net-new** work that is **not** in #1130:

- **Strict `lossless_only` mode** — keeps lossless tabular compaction,
but routes every path that would need a CCR marker (row-drop sentinel
**and** opaque-blob offload) to leave content uncompacted instead, so
output is always marker-free **and** byte-recoverable.
- **Python parity** — `lossless_only` exposed across both config
dataclasses, a `SmartCrusher` kwarg, and a per-call `crush(...,
lossless_only=)` override.
- **`HEADROOM_LOSSLESS_ONLY` env var** — wires the mode through to the
proxy runtime so real agents can use it.

The #1130 opaque gate is consumed here through a single centralized
helper (`opaque_markers_enabled() = enable_ccr_marker &&
!lossless_only`) used by **all four** `ClassifyConfig` construction
sites.

## Type of Change

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

## Changes Made

- **`feat(smart_crusher)`** — Add `lossless_only` (default `false`):
keeps lossless tabular compaction but routes every marker-requiring path
(row-drop sentinel + opaque-blob offload) to leave content uncompacted
instead. Exposed across the Rust core, PyO3 bridge, both Python config
dataclasses, a `SmartCrusher` kwarg, a per-call `crush(...,
lossless_only=)` override, and `smart_crush_tool_output`. Includes a
`debug_assert` documenting the load-bearing invariant (a `lossless_only`
crusher must never reach the CCR store write).
- **`refactor(smart_crusher)`** — Extract
`SmartCrusherConfig::opaque_markers_enabled()` as the single source of
truth for `enable_ccr_marker && !lossless_only`, consumed by **all
four** `ClassifyConfig` sites: the compaction-stage builder,
`with_compaction_format`, the top-level `process_string` path (Rust
core), and the PyO3 `compact_document_json` document-compactor path. No
site derives the gate inline anymore, so they cannot drift.
- **`feat(proxy)`** — Expose the mode via `HEADROOM_LOSSLESS_ONLY`:
`ContentRouterConfig.smart_crusher_lossless_only` →
`_get_smart_crusher`; the proxy reads the env var and sets it on the
live router config. Previously reachable only via the Python API, never
through the proxy.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check` on changed files)
- [ ] Type checking passes (`mypy headroom`) — not run (see Additional
Notes)
- [x] New tests added for new functionality
- [x] Manual testing performed (proxy env-var seam, end-to-end — see
Real Behavior Proof)

### Test Output

```text
### RUST  (cargo test -p headroom-core --lib smart_crusher)
test result: ok. 323 passed; 0 failed; 0 ignored; 0 measured; 516 filtered out

### PYTEST  (test_smart_crusher_bugs.py + test_agent_savings.py + test_smart_crusher_toin_attachment.py)
45 passed

### RUFF  (changed files)
All checks passed!

### FMT + CLIPPY  (cargo fmt --check && cargo clippy --workspace --lib)
clean — no warnings
```

New/affected tests: `enable_ccr_marker_false_suppresses_opaque_markers`,
`lossless_only_leaves_array_uncompacted_instead_of_dropping`,
`lossless_only_inlines_opaque_blobs_when_table_ships`,
`lossless_only_never_writes_to_ccr_store` (Rust);
`TestLosslessOnlyMode`,
`test_router_lossless_only_flag_reaches_crusher`,
`test_router_lossless_only_defaults_off` (Python). Coexists green with
#1130's `long_string_stays_scalar_when_opaque_markers_disabled` (Rust)
and `test_smart_crusher_toin_attachment.py` (Python). The Python
`TestOpaqueMarkerGate` from the dropped gating commit was removed as
redundant with #1130's coverage.

## Real Behavior Proof

### Proxy env-var seam — end-to-end (this revision)

The one path with no automated coverage was `server.py` reading
`HEADROOM_LOSSLESS_ONLY` from the environment and threading it into the
live router config. Verified end-to-end by instantiating the **real**
`HeadroomProxy`, pulling the `ContentRouter` out of its pipeline, and
crushing a 50-row array with >256B opaque cells through the real Rust
crusher:

| | `HEADROOM_LOSSLESS_ONLY=1` | env unset (default) |
|---|---|---|
| `crusher._lossless_only` | **True** | **False** |
| output contains `<<ccr:` | **No** (marker-free) | Yes (normal lossy) |
| byte-recoverable (round-trips to original JSON) | **Yes** | No (rows
offloaded) |

This confirms the full chain `os.environ["HEADROOM_LOSSLESS_ONLY"]` →
`server.py` → `ContentRouterConfig.smart_crusher_lossless_only` →
`content_router.py` → `crusher_config.lossless_only` → Rust crusher. The
default column proves strict mode genuinely changes behavior (not a
no-op) and that the default path is unchanged.

### Prior live-traffic run

- Environment: Headroom proxy in front of a real agent (Hermes) routed
to NVIDIA NIM (OpenAI-compatible upstream). Isolated config dir;
`OPENAI_TARGET_API_URL=https://integrate.api.nvidia.com/v1`,
`HEADROOM_LOSSLESS_ONLY=1`. Confirmed with `ss` that all LLM traffic
flowed agent → proxy → upstream with no direct bypass.
- Exact command / steps: Start the proxy with `python -m
headroom.proxy.server --host 127.0.0.1 --port 8787`; point the agent's
LLM base_url at `http://127.0.0.1:8787/v1`; run a real chat plus a
`search_files`-style task; read `/stats` and `/v1/retrieve/stats`; then
toggle `HEADROOM_LOSSLESS_ONLY` and repeat for the comparison.
- Observed result: With 150K+ tokens of real traffic processed,
`lossless_only` kept the CCR store empty (`entry_count: 0`) and emitted
zero markers. A synthetic before/after with opaque (>256B) cells
produced 12 `<<ccr:>>` markers in default mode and 0 under
`lossless_only`, with output round-tripping to the original JSON
structure.
- Not tested: A live `lossless_only`-vs-markers contrast on real agent
traffic. The SmartCrusher offload path never engaged on this agent's
tool outputs (`total_compressions: 0`; CCR store stayed at `entry_count:
0` even after a broad codebase search), and compression stayed marginal
(~0.2–0.4%) in both modes. The agent's tool results don't match the
crushable-array profile the offload paths target, so the marker path is
never exercised in that integration. Why SmartCrusher barely engages
with this agent's outputs is a separate integration question (output
format / routing / size thresholds), out of scope for this change.

## 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 — N/A
(config docstrings updated in-tree; no separate docs)
- [x] My changes generate no new warnings
- [x] I have added tests that prove my feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable — N/A

## Additional Notes

- Rebased on top of merged #1130; the now-redundant opaque-blob gating
commit was dropped, so this PR is purely the `lossless_only` feature +
proxy wiring on top of #1130's gate.
- `mypy headroom` was not run in this environment; happy to add the
result if CI requires it.
- Default behavior is fully preserved: `enable_ccr_marker` defaults to
`true`, `lossless_only` defaults to `false`, and
`HEADROOM_LOSSLESS_ONLY` unset is a no-op.
2026-06-23 12:52:15 -05:00
..
__init__.py Initial commit: Headroom SDK - LLM context optimization toolkit 2026-01-06 23:16:58 -08:00
test_code_compressor.py fix(code): verify a real parse in tree-sitter availability check (#1231) (#1299) 2026-06-22 23:00:57 -05:00
test_content_router.py feat(proxy): support glob patterns in exclude_tools (#870) (#1259) 2026-06-22 18:55:22 -05:00
test_detect_fallback_1123.py fix(router): degrade to pure-Python detection on native panic (#1123) (#1260) 2026-06-22 18:57:05 -05:00
test_diff_compressor.py feat(rust): retire python diff_compressor, ship rust-only via pyo3 2026-04-26 09:15:37 -07:00
test_diff_compressor_rust_parity.py feat(rust): retire python diff_compressor, ship rust-only via pyo3 2026-04-26 09:15:37 -07:00
test_html_extractor.py fix(tests): skip HTML extractor tests when trafilatura not installed 2026-01-31 15:39:55 -08:00
test_kompress_compressor.py fix(proxy): make Kompress eager preload cache-only so a cold cache can't block startup (#783) 2026-06-11 12:53:03 -05:00
test_kompress_deadline.py perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
test_kompress_size_gate.py perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
test_ort_dylib.py Pin ORT dylib on Windows; init Python logging (#1010) 2026-06-23 07:46:24 -05:00
test_read_lifecycle.py feat(transforms): attribute read_lifecycle + smart_crush tags (#249) 2026-06-11 11:51:26 -05:00
test_smart_crusher_attribution.py feat(transforms): attribute read_lifecycle + smart_crush tags (#249) 2026-06-11 11:51:26 -05:00
test_smart_crusher_bugs.py Marker-free lossless_only mode + gate opaque-blob CCR markers behind enable_ccr_marker (#1129) 2026-06-23 12:52:15 -05:00
test_smart_crusher_ccr_roundtrip.py chore(rust): SmartCrusher CCR marker injection + walker unification 2026-04-27 20:25:22 -07:00
test_smart_crusher_lossless_default.py feat(rust): SmartCrusher PR4 — lossless-first default + CCR-Dropped restoration 2026-04-27 16:30:22 -07:00
test_smart_crusher_rust_parity.py feat(rust): SmartCrusher PR4 — lossless-first default + CCR-Dropped restoration 2026-04-27 16:30:22 -07:00
test_tag_protector.py fix: A9 — tag protector discards wrap on placeholder loss 2026-05-02 18:01:24 -07:00
test_text_crusher.py perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
test_text_crusher_parity.py perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
test_text_crusher_routing.py perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
test_tree_sitter_thread_safety.py fix(transforms): use thread-local tree-sitter parsers to prevent pyo3 Unsendable panic (#604) 2026-06-10 18:30:00 -05:00