# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a-memory-tool-s-forget-as-a-deletion-versus-a-tombstone-the-erasure-retention-interleaving-boundary.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a-memory-tool-s-forget-as-a-deletion-versus-a-tombstone-the-erasure-retention-interleaving-boundary.warrant.json
# Markdown SHA-256:   7c1a2c960c8cd496ae9ebcdd061fb01ac5730d9e8c416be568317ba4982460a7
# Code AST SHA-256:   d15810704cfb030730e2b1bdcb89e9796856f6a2ff37d86ecaac332bf9e6181b
# Verified Classes:   ErasureRetentionViolation, ForgetResult
# Defensive Guards:   2 fail-closed invariants (zero external deps)

import time
from dataclasses import dataclass


class ErasureRetentionViolation(RuntimeError):
    """Raised when a forgotten key is observed readable or merged against a ghost."""
    pass


@dataclass
class ForgetResult:
    key: str
    tombstone_seq: int
    returned_at: float
    shard_lag_ms: int
    contract_guarantees_sync_tombstone: bool


def forget_and_verify(memory, key: str, *, max_wait_ms: int = 2000) -> None:
    """Forget a key, then enforce the erasure-retention boundary.

    Safe only when the shard contract guarantees synchronous tombstone
    visibility AND tombstone-aware sweeping. Otherwise quarantine: do not
    write to the key, re-issue forget idempotently, and verify after lag.
    """
    result: ForgetResult = memory.forget(key)

    if result.contract_guarantees_sync_tombstone:
        # Fast path: tombstone is visible on the read path already.
        if memory.read(key) is not None:
            raise ErasureRetentionViolation(
                f"forget({key!r}) returned success but read still resolves; "
                f"contract claimed sync tombstone, seq={result.tombstone_seq}"
            )
        return

    # Quarantine path: wait out the propagation horizon, never write.
    deadline = time.monotonic() + max_wait_ms / 1000.0
    while time.monotonic() < deadline:
        if memory.read(key) is None:
            return
        # Re-issue forget idempotently; do NOT write to the key here.
        memory.forget(key, idempotency_token=f"reforget:{key}:{result.tombstone_seq}")
        time.sleep(result.shard_lag_ms / 1000.0)

    ghost = memory.read(key)
    raise ErasureRetentionViolation(
        f"forget({key!r}) is a tombstone, not a deletion: key still readable "
        f"after {max_wait_ms}ms (lag={result.shard_lag_ms}ms, "
        f"ghost={ghost!r}). Do not write to this key; reconcile tombstone first."
    )
