# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-pointer-resurrection-across-hierarchical-compaction-checkpoints.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-pointer-resurrection-across-hierarchical-compaction-checkpoints.warrant.json
# Markdown SHA-256:   6fcd9dba190218db5b03233d351925a5fb630654ed46edb3fe4c9c156685fe63
# Code AST SHA-256:   bfdf159eb16bb3e535332071aa667fdacef7947a2f8d83eece086b3b38bcc333
# Verified Classes:   PointerResurrectionError, Level, Pointer, Node
# Defensive Guards:   4 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from enum import IntEnum


class PointerResurrectionError(RuntimeError):
    """Raised when a retrieval pointer resolves to a node at the wrong level.

    This is the failure mode described above: a pointer minted against an L0
    leaf re-resolves against an L1 summary that reused the same span_id
    namespace. Silent if unguarded; loud if this exception is raised.
    """


class Level(IntEnum):
    L0_VERBATIM = 0
    L1_SUMMARY = 1
    L2_ROLLUP = 2


@dataclass(frozen=True)
class Pointer:
    span_id: str
    level: Level
    content_hash: str
    epoch: int
    accept_summary: bool = False


@dataclass(frozen=True)
class Node:
    span_id: str
    level: Level
    content: str
    content_hash: str
    epoch: int


def resolve(pointer: Pointer, tree: dict[str, Node], current_epoch: int) -> str:
    node = tree.get(pointer.span_id)
    if node is None:
        raise PointerResurrectionError(
            f"span {pointer.span_id!r} unreachable in epoch {current_epoch}"
        )
    if pointer.epoch != current_epoch and node.epoch != pointer.epoch:
        raise PointerResurrectionError(
            f"pointer epoch {pointer.epoch} stale vs current {current_epoch} "
            f"for span {pointer.span_id!r}"
        )
    if node.level != pointer.level:
        if pointer.level == Level.L0_VERBATIM and pointer.accept_summary:
            return node.content
        raise PointerResurrectionError(
            f"level mismatch for span {pointer.span_id!r}: "
            f"pointer wants {pointer.level.name}, node is {node.level.name}"
        )
    if node.content_hash != pointer.content_hash:
        raise PointerResurrectionError(
            f"hash mismatch for span {pointer.span_id!r}: "
            f"pointer {pointer.content_hash} != node {node.content_hash}"
        )
    return node.content
