# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-model-driven-context-rewriting-as-destructive-rather-than-compressive.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-model-driven-context-rewriting-as-destructive-rather-than-compressive.warrant.json
# Markdown SHA-256:   e816d1b77a162c9b920dc2c14690aa9534d21df1ba72050cb1998b7be88b0be4
# Code AST SHA-256:   2a198284670a1c4d1b0f4fc908aca64f456d88f5fd8255ba8978dbca9f9cc0f6
# Verified Classes:   DestructiveContextRewriteError
# Defensive Guards:   4 fail-closed invariants (zero external deps)

class DestructiveContextRewriteError(RuntimeError):
    """Raised when a self-emitted context rewrite removes material that
    cannot be re-derived from outside the post-rewrite window."""


REEXECUTABLE_KINDS = frozenset({"tool_call", "store_key", "url"})


def validate_self_edit(block: dict, rewrite: dict) -> None:
    """Gate a model-emitted prune. `block` is the target; `rewrite` is the
    model's proposal, which must carry a recoverability handle for anything
    it removes that is not pure redundancy."""
    if rewrite.get("kind") == "redundancy":
        return

    removed = set(block.get("facts", [])) - set(rewrite.get("retained_facts", []))
    if not removed:
        return

    handle = rewrite.get("recover_handle")
    if handle is None:
        raise DestructiveContextRewriteError(
            f"prune removes {sorted(removed)} with no recover_handle; "
            "post-edit window cannot falsify its own summary"
        )

    if handle.get("kind") not in REEXECUTABLE_KINDS:
        raise DestructiveContextRewriteError(
            f"recover_handle kind {handle.get('kind')!r} is not re-executable; "
            "source may be a one-shot side effect"
        )

    if block.get("contains_negative_result") or block.get("is_only_witness"):
        raise DestructiveContextRewriteError(
            "block holds a negative result or is the sole witness of a fact; "
            "absence is unfalsifiable after pruning"
        )

    if rewrite.get("justification_source") == "self":
        raise DestructiveContextRewriteError(
            "rewrite justifies itself using the content it prunes (circular); "
            "require an external anchor"
        )
