# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-does-an-instance-detect-revocation-of-a-memory-record-it-has-already-materialized-into-a-working-set.md
# Remnant Warrant:    https://corpusfetch.com/c/how-does-an-instance-detect-revocation-of-a-memory-record-it-has-already-materialized-into-a-working-set.warrant.json
# Markdown SHA-256:   305d02496e2ddd003aef5db3b28f249ff03416cffdbce4e97f8711c2e6136a55
# Code AST SHA-256:   ac51d0cd57a9d07f6fa63bf1c52a5c52edfe4a300702bd0e87d2a951c9cb9e66
# Verified Classes:   RevocationDetectedError, Materialized
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import Optional


class RevocationDetectedError(RuntimeError):
    """Raised when a materialized record is used after it (or its lineage) was revoked."""


@dataclass(frozen=True)
class Materialized:
    record_id: str
    version: int
    content: str
    provenance_source: Optional[str]


def assert_still_valid(store, rec: Materialized, *, irreversible: bool) -> None:
    """
    Call immediately before using `rec` to authorize a side effect.
    `store` must expose current_version(record_id) and is_revoked(record_id).
    """
    if not irreversible:
        return  # reversible action: local copy acceptable per decision boundary

    current = store.current_version(rec.record_id)
    if current is None or store.is_revoked(rec.record_id):
        raise RevocationDetectedError(
            f"record {rec.record_id} revoked after read (read version={rec.version})"
        )
    if current != rec.version:
        raise RevocationDetectedError(
            f"record {rec.record_id} version drift: read={rec.version} current={current}"
        )
    if rec.provenance_source is not None:
        src = rec.provenance_source
        if store.is_revoked(src):
            raise RevocationDetectedError(
                f"record {rec.record_id} has revoked provenance source {src}"
            )
