# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a-verified-deletion-as-an-erasure-proof-rather-than-a-tombstone.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a-verified-deletion-as-an-erasure-proof-rather-than-a-tombstone.warrant.json
# Markdown SHA-256:   5cb6d62b85048d549760c2086937513edf6e6ca1dcdb52c85da3d80b245832c4
# Code AST SHA-256:   6792c8e9cfeda11b6cbd8a36dc77976fcdbe5fad4a8cc11fab9c0b1ebbf9b618
# Verified Classes:   DerivationClosureError
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class DerivationClosureError(RuntimeError):
    """Raised when an erasure proof's commitment domain does not cover
    the full derivation closure of the target record."""
    pass


def attest_erasure(record_id, proof, compliance_boundary, derived_stores):
    # The proof must commit over every store in the declared boundary.
    covered = set(proof.commitment_domain)
    required = set(compliance_boundary)

    missing = required - covered
    if missing:
        raise DerivationClosureError(
            f"Erasure proof for {record_id!r} does not cover stores: "
            f"{sorted(missing)}. Cannot attest erasure; downgrade to tombstone."
        )

    # Derived artifacts must be enumerated via lineage. Absence of lineage
    # means the closure cannot be computed, so attestation is refused.
    for store in derived_stores:
        if store.lineage is None:
            raise DerivationClosureError(
                f"Derived store {store.name!r} has no lineage to {record_id!r}; "
                f"derivation closure is unknown and erasure cannot be proven."
            )
        if not store.supports_provable_deletion:
            raise DerivationClosureError(
                f"Derived store {store.name!r} only supports eventual deletion "
                f"(rebuild window {store.rebuild_window_s}s); erasure gap exists."
            )

    return {
        "record_id": record_id,
        "attestation": "erasure",
        "commitment_domain": sorted(covered),
        "residual_risk": [],
    }
