headroom/tests/test_transforms
chopratejas 6aacd4805a fix: A9 — tag protector discards wrap on placeholder loss
When a placeholder is lost during compression, restore_tags now
discards the wrap rather than appending the original tag at the
trailing edge of the output. The old "append" fallback emitted
malformed XML — an opening tag with no body and no closing tag —
on ~350 production requests over 9 days. Per the proxy log
findings, the corruption pattern was `compressed-stuff <tag>`,
which downstream models interpret as a truncated message.

Concrete changes:

* `crates/headroom-core/src/transforms/tag_protector.rs`:
  - `restore_tags` no longer accumulates `tail_appends`. Lost
    placeholders are silently dropped from the output bytes.
  - New `restore_tags_with_request_id` entry point threads an
    optional request id into the structured ERROR log so the
    proxy layer can wire request context end-to-end. PyO3 binding
    keeps the existing 2-arg signature (no Python caller has a
    request id today).
  - `tag_lost_warn` is replaced by `tag_lost_error`. Severity
    moves from WARN to ERROR with structured fields
    (`event=tag_protector_placeholder_lost`, `tag_preview`,
    `compressed_length`, `action=discarded_wrap`, optional
    `request_id`) so operators can alert on the corruption rather
    than have it disappear into a WARN line.
  - `parse_tag_at` gained a bounds check after consuming a
    leading '/' — proptest discovered an OOB on input `</`.
  - The old `restore_lost_placeholder_appended` test (which
    pinned the broken behavior) is replaced with three positive
    tests: wrap-discard, idempotence on full loss, and
    partial-loss-keeps-present-drops-lost.
  - New proptest suite enforces three invariants over arbitrary
    inputs: no introduced asymmetry, idempotence on full
    placeholder loss, and no orphan-byte injection.

* `headroom/transforms/tag_protector.py`: docstring updated
  to document the discard-wrap semantics — the prior text
  ("appended on the trailing edge") is now incorrect.

* `tests/test_tag_protector_invariant.py` (new): Python-side
  invariant suite that exercises the same three properties
  end-to-end through the public Python API. Uses a deterministic
  seeded random walk (no `hypothesis` dependency) so CI is stable
  and reproducible.

* `tests/test_transforms/test_tag_protector.py`: replaces the
  broken-behavior test with the new wrap-discard semantics.

Per-finding-#3: ~/Desktop/HEADROOM_PROXY_LOG_FINDINGS_2026_05_03.md.
2026-05-02 18:01:24 -07:00
..
__init__.py Initial commit: Headroom SDK - LLM context optimization toolkit 2026-01-06 23:16:58 -08:00
test_code_compressor.py Fix CI test, bump to 0.5.7 2026-03-26 12:04:30 -07:00
test_content_router.py Fix tests: update default assertions for disabled CodeCompressor 2026-04-11 10:40:44 -07: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 Make KompressCompressor model-configurable: model_id, chunk_words, score_threshold 2026-04-14 13:19:35 -07:00
test_read_lifecycle.py fix: dashboard metrics, TTFB tracking, eager LLMLingua loading, and multi-provider consistency 2026-03-07 23:33:45 -08:00
test_smart_crusher_bugs.py feat(rust): retire python smart_crusher, ship rust-only via pyo3 2026-04-27 00:52:21 -07: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