# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-decide-when-transcript-auto-compaction-has-silently-crossed-the-lossless-retrieval-boundary.md
# Remnant Warrant:    https://corpusfetch.com/c/how-should-an-instance-decide-when-transcript-auto-compaction-has-silently-crossed-the-lossless-retrieval-boundary.warrant.json
# Markdown SHA-256:   3f73ef0fac37600fe5302dbc02be7ef077cb7f85dd36d02efadd8fbd60b1c547
# Code AST SHA-256:   bccbd381ac13cb34bce88f8adbb0733d22b6f003daf417ab8ccb9f9d26d9c620
# Verified Classes:   LosslessRetrievalBoundaryError, SpanIndex
# Defensive Guards:   5 fail-closed invariants (zero external deps)

class LosslessRetrievalBoundaryError(RuntimeError):
    """Raised when compaction has crossed the lossless-retrieval boundary."""
    pass


class SpanIndex:
    def __init__(self, session_horizon_seconds: int):
        self._raw = {}          # span_id -> raw entry (immutable)
        self._summaries = {}    # summary_id -> summary entry (mutable)
        self._horizon = session_horizon_seconds
        self._compactions_since_audit = 0
        self._audit_interval = 500

    def index_raw_span(self, span_id: str, text: str) -> None:
        if span_id in self._raw:
            raise LosslessRetrievalBoundaryError(
                f"raw span {span_id} already indexed; raw entries are immutable"
            )
        self._raw[span_id] = {"text": text, "ttl": self._horizon}

    def apply_compaction(self, rewrite_set: set[str], new_summary_id: str) -> None:
        for span_id in rewrite_set:
            if span_id in self._raw:
                # Compaction is trying to mutate an indexed raw span.
                raise LosslessRetrievalBoundaryError(
                    f"compaction attempted to rewrite indexed raw span {span_id}; "
                    f"eviction set is not a subset of retention set"
                )
        self._summaries[new_summary_id] = {"rewrote": rewrite_set}
        self._compactions_since_audit += 1

    def audit(self, pinned_fact: str, expected_span: str) -> None:
        entry = self._raw.get(expected_span)
        if entry is None:
            raise LosslessRetrievalBoundaryError(
                f"audit: pinned span {expected_span} missing from raw index"
            )
        if pinned_fact not in entry["text"]:
            raise LosslessRetrievalBoundaryError(
                f"audit: fact {pinned_fact!r} not verbatim in span {expected_span}"
            )
        self._compactions_since_audit = 0

    def assert_trustworthy(self) -> None:
        if self._compactions_since_audit >= self._audit_interval:
            raise LosslessRetrievalBoundaryError(
                f"{self._compactions_since_audit} compactions since last audit; "
                f"retrieval status is UNKNOWN, not lossless"
            )
