# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/when-must-an-instance-refuse-a-scoped-read-the-retention-epoch-inversion-in-read-write-forget-memory-apis.md
# Remnant Warrant:    https://corpusfetch.com/c/when-must-an-instance-refuse-a-scoped-read-the-retention-epoch-inversion-in-read-write-forget-memory-apis.warrant.json
# Markdown SHA-256:   08a639963c6e09f0aa6f3c1cb4df1d720b3c19b11a0a94fbae4d42fdb30c5ea6
# Code AST SHA-256:   7b67db7b5dfc06624acb26e9817bd987e6772c604b7250ef6bb980b33bb1aea3
# Verified Classes:   RetentionEpochInversion, ScopeToken, Store
# Defensive Guards:   4 fail-closed invariants (zero external deps)

import time
from dataclasses import dataclass


class RetentionEpochInversion(RuntimeError):
    """Raised when a scoped read would return data older than the caller's forget epoch."""
    pass


@dataclass(frozen=True)
class ScopeToken:
    scope: str
    minted_epoch: int  # forget_epoch observed at token issuance


@dataclass
class Store:
    applied_epoch: int
    records: dict  # key -> (value, tombstoned_at_epoch)


def scoped_read(store: Store, token: ScopeToken, key: str) -> str:
    # Rule: never serve a read from a store that lags the token's mint epoch.
    if token.minted_epoch > store.applied_epoch:
        raise RetentionEpochInversion(
            f"store at epoch {store.applied_epoch} lags token minted at "
            f"{token.minted_epoch}; read of {key!r} in scope {token.scope!r} "
            f"may expose forgotten data — retry against caught-up replica or primary"
        )
    entry = store.records.get(key)
    if entry is None:
        raise KeyError(key)
    value, tombstoned_at = entry
    if tombstoned_at is not None and tombstoned_at <= store.applied_epoch:
        raise RetentionEpochInversion(
            f"record {key!r} tombstoned at epoch {tombstoned_at}, "
            f"store applied {store.applied_epoch}; refusing to serve"
        )
    return value


def forget(store: Store, key: str) -> int:
    """Tombstone a record and return the convergence epoch callers must wait for."""
    store.records[key] = (store.records[key][0], store.applied_epoch + 1)
    store.applied_epoch += 1
    return store.applied_epoch


def verify_forget(store: Store, key: str, convergence_epoch: int) -> bool:
    if store.applied_epoch < convergence_epoch:
        raise RetentionEpochInversion(
            f"verification read at epoch {store.applied_epoch} < convergence "
            f"epoch {convergence_epoch}; forget not yet converged on this replica"
        )
    return store.records.get(key, (None, None))[1] is not None
