# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a-model-driven-context-pruning-decision-as-reversible-and-when-does-pruning-cross-into-lossy-commitment.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a-model-driven-context-pruning-decision-as-reversible-and-when-does-pruning-cross-into-lossy-commitment.warrant.json
# Markdown SHA-256:   a9f72a8482c597927afd86cb66bed0b499effaa0472d73c028b36377c80fe920
# Code AST SHA-256:   fb9645394061c256230597d6597ecab576212a587e25248dfedd29e6cfa58f52
# Verified Classes:   IrreversiblePruneError
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class IrreversiblePruneError(RuntimeError):
    """Raised when a model-driven prune would free a span that the runtime
    cannot prove is recoverable (existence + addressability + provenance)."""
    pass


def execute_prune(directive, backing_store, window):
    span_id = directive["span_id"]
    span = window.get(span_id)
    if span is None:
        raise IrreversiblePruneError(f"span {span_id} not in active window")

    # Runtime-side check: never trust the model's own reversibility claim.
    handle = backing_store.handle_for(span_id)
    recoverable = (
        backing_store.exists(handle)              # condition 1: existence
        and handle is not None                    # condition 2: addressability
        and backing_store.distinguishes_absent()  # condition 3: provenance
    )

    if recoverable:
        window.drop(span_id)
        window.index.add(handle)
        return {"status": "reversible", "handle": handle}

    if directive.get("accept_lossy") is True:
        window.drop(span_id)
        window.index.add_tombstone(span_id, contents="unknown")
        return {"status": "lossy", "tombstone": span_id}

    # Model asked to prune but did not accept irreversibility, and the
    # runtime cannot prove recoverability. Reject; do not silently free.
    raise IrreversiblePruneError(
        f"prune of {span_id} is not provably reversible "
        f"(existence={backing_store.exists(handle)}, "
        f"addressable={handle is not None}, "
        f"provenance={backing_store.distinguishes_absent()}); "
        f"model must set accept_lossy=True or retain the span"
    )
