{"record":{"id":"618289974d74e38d","repo":"headroomlabs-ai/headroom","slug":"smartcrusher-custom-relevance-config-scorer","errorCode":null,"errorMessage":"SmartCrusher: custom `relevance_config` / `scorer` overrides are not yet supported by the Rust-backed implementation. Pass `None` to use the default HybridScorer. Tracked in RUST_DEV.md; full support lands with Stage 3c.2's relevance-crate Python bridge.","messagePattern":"SmartCrusher: custom `relevance_config` / `scorer` overrides are not yet supported by the Rust-backed implementation\\. Pass `None` to use the default HybridScorer\\. Tracked in RUST_DEV\\.md; full support lands with Stage 3c\\.2's relevance-crate Python bridge\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"headroom/transforms/smart_crusher.py","lineNumber":346,"sourceCode":"        # opaque-string CCR substitutions still emit always — they have\n        # no Python equivalent and no production caller has asked for\n        # them to be suppressed.\n        if ccr_config is None:\n            self._ccr_config = CCRConfig()\n        else:\n            self._ccr_config = ccr_config\n\n        # `relevance_config` and `scorer` remain in the signature for\n        # source compatibility, but the Rust port doesn't support\n        # overrides yet (it always uses `HybridScorer` from the\n        # relevance crate; the Python-bridged constructor surface\n        # arrives in Stage 3c.2). Silently dropping a user-supplied\n        # scorer would be a textbook silent fallback — if a caller\n        # depends on a custom scoring function and we ignore it, the\n        # compression they get back is wrong in a way they cannot see.\n        # Fail loud instead. See `feedback_no_silent_fallbacks.md`.\n        if relevance_config is not None or scorer is not None:\n            raise NotImplementedError(\n                \"SmartCrusher: custom `relevance_config` / `scorer` \"\n                \"overrides are not yet supported by the Rust-backed \"\n                \"implementation. Pass `None` to use the default \"\n                \"HybridScorer. Tracked in RUST_DEV.md; full support \"\n                \"lands with Stage 3c.2's relevance-crate Python bridge.\"\n            )\n\n        # Lazy TOIN handle. Loaded on first compression that has items\n        # to learn from. Skipping import at __init__ keeps cold-start\n        # fast for environments where telemetry is disabled.\n        self._toin: Any = None\n        self._toin_load_failed = False\n\n        # F2.2: per-request CompressionPolicy, set from\n        # ``kwargs[\"compression_policy\"]`` at the start of ``apply()``\n        # and read by ``_record_to_toin`` to gate TOIN writes when\n        # ``policy.toin_read_only`` is true (Subscription mode).\n        # Defaults to ``None`` so the direct ``crush()`` / ``crush_array_json()``","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/transforms/smart_crusher.py#L328-L364","documentation":"SmartCrusher was ported to a Rust backend that always uses the crate's built-in HybridScorer; the `relevance_config` and `scorer` constructor parameters remain in the signature for source compatibility but any non-None value raises NotImplementedError. The project's no-silent-fallbacks policy means a custom scorer you supplied will never be silently ignored — it fails loud instead. Full support is tracked for Stage 3c.2 (relevance-crate Python bridge).","triggerScenarios":"Constructing `SmartCrusher(relevance_config=cfg)` or `SmartCrusher(scorer=my_scorer)` on the Rust-backed version — anything other than leaving both as None.","commonSituations":"Migrating existing code from the pre-Rust Python SmartCrusher that supported custom scoring; copying older examples/docs that pass a relevance config; a dependency upgrade pulled the Rust-backed headroom version in transitively.","solutions":["Pass `None` for both parameters and use the default HybridScorer (the intended current usage)","Pin your dependency to the last Python-backed SmartCrusher release until Stage 3c.2 ships the relevance-crate Python bridge","If you must influence scoring now, check whether the supported knobs (e.g. lossless_only, compaction options) cover your use case instead of a custom scorer"],"exampleFix":"# before\ncrusher = SmartCrusher(relevance_config=my_cfg, scorer=my_fn)\n\n# after\ncrusher = SmartCrusher()  # default HybridScorer from the relevance crate\n# dependency pin if custom scoring is required:\n#   pip install \"headroom-ai==<last-python-backed-version>\"","handlingStrategy":"type-guard","validationCode":"def supports_custom_scorer() -> bool:\n    # Stage 3c.2 (relevance-crate Python bridge) is the gate\n    return False  # update when the bridge ships\n\nif my_scorer is not None and not supports_custom_scorer():\n    raise RuntimeError(\"custom scorer unsupported; refusing to configure SmartCrusher\")","typeGuard":"from dataclasses import is_dataclass\n\ndef smart_crusher_args_safe(relevance_config, scorer) -> bool:\n    return relevance_config is None and scorer is None","tryCatchPattern":"try:\n    crusher = SmartCrusher(relevance_config=cfg, scorer=fn)\nexcept NotImplementedError:\n    crusher = SmartCrusher()  # explicit downgrade decision, logged\n    log.warning(\"custom scorer dropped: Rust-backed SmartCrusher\")","preventionTips":["Feature-detect at startup and refuse to run (or explicitly downgrade with a log) if custom scoring is configured","Pin the dependency version in requirements so the Rust port does not arrive as a surprise upgrade","Track RUST_DEV.md / Stage 3c.2 before reintroducing custom scorers"],"tags":["not-implemented","rust-port","breaking-change","smart-crusher"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}