# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/where-must-an-instance-of-the-memory-tool-fail-closed-when-gdpr-aligned-deletion-races-ttl-eviction-and-scoped-retrieval.md
# Remnant Warrant:    https://corpusfetch.com/c/where-must-an-instance-of-the-memory-tool-fail-closed-when-gdpr-aligned-deletion-races-ttl-eviction-and-scoped-retrieval.warrant.json
# Markdown SHA-256:   142b50c895d9e4bad22069d827a99f49ca6204084ce3311444a8e9d95597fd35
# Code AST SHA-256:   935b9c332f3d8a905c5ae2952cc08beb4a5ea4b5534126971cf685287b66940e
# Verified Classes:   TombstoneType, ErasureRaceError, Tombstone, MemoryStore
# Defensive Guards:   5 fail-closed invariants (zero external deps)

from __future__ import annotations

import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional


class TombstoneType(str, Enum):
    EVICTED = "evicted"      # TTL lifecycle event; may be re-derived
    ERASED = "erased"        # GDPR erasure; must never be resurrected


class ErasureRaceError(RuntimeError):
    """Raised when a read would disclose a record whose erasure committed mid-flight."""


@dataclass
class Tombstone:
    record_id: str
    type: TombstoneType
    erasure_epoch: int
    committed_at: float = field(default_factory=time.monotonic)


@dataclass
class MemoryStore:
    records: dict[str, dict] = field(default_factory=dict)
    tombstones: dict[str, Tombstone] = field(default_factory=dict)
    _epoch: int = 0

    def write(self, record_id: str, value: dict) -> None:
        tomb = self.tombstones.get(record_id)
        if tomb is not None and tomb.type is TombstoneType.ERASED:
            # Fail closed: an erased id is not a reusable slot.
            raise ErasureRaceError(
                f"refusing to write {record_id!r}: id is under GDPR erasure"
            )
        self.records[record_id] = value

    def forget(self, record_id: str) -> Tombstone:
        existing = self.tombstones.get(record_id)
        if existing is not None and existing.type is TombstoneType.ERASED:
            return existing  # idempotent: return original receipt
        self._epoch += 1
        tomb = Tombstone(record_id, TombstoneType.ERASED, self._epoch)
        self.tombstones[record_id] = tomb
        self.records.pop(record_id, None)
        return tomb

    def evict(self, record_id: str) -> None:
        if record_id in self.tombstones:
            return  # never downgrade an erasure to an eviction
        self._epoch += 1
        self.tombstones[record_id] = Tombstone(
            record_id, TombstoneType.EVICTED, self._epoch
        )
        self.records.pop(record_id, None)

    def read(self, record_id: str) -> dict:
        tomb = self.tombstones.get(record_id)
        if tomb is not None and tomb.type is TombstoneType.ERASED:
            raise ErasureRaceError(f"{record_id!r} is erased")
        snapshot_epoch = self._epoch
        value = self.records.get(record_id)
        if value is None:
            raise KeyError(record_id)
        buffered = dict(value)
        # Revalidate at the flush boundary: erasure may have committed mid-read.
        current = self.tombstones.get(record_id)
        if current is not None and current.type is TombstoneType.ERASED:
            if current.erasure_epoch > snapshot_epoch:
                raise ErasureRaceError(
                    f"erasure of {record_id!r} committed during read; aborting flush"
                )
        return buffered

    def repair(self, record_id: str, replica_value: dict) -> None:
        tomb = self.tombstones.get(record_id)
        if tomb is not None and tomb.type is TombstoneType.ERASED:
            # Anti-entropy must never resurrect an erased record.
            raise ErasureRaceError(
                f"anti-entropy refused to restore erased {record_id!r}"
            )
        self.records[record_id] = replica_value


def demonstrate_race() -> None:
    store = MemoryStore()
    store.write("subj-42/note-1", {"ssn": "000-00-0000"})
    # Reader opens snapshot, then erasure commits before flush.
    snapshot = store.records["subj-42/note-1"]
    store.forget("subj-42/note-1")
    try:
        store.read("subj-42/note-1")
    except ErasureRaceError as exc:
        print(f"blocked at flush boundary: {exc}")
    # A lagging replica repair must also fail closed.
    try:
        store.repair("subj-42/note-1", snapshot)
    except ErasureRaceError as exc:
        print(f"blocked at repair: {exc}")


if __name__ == "__main__":
    demonstrate_race()
