# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-detect-that-hierarchical-summarization-has-crossed-the-fidelity-cliff-before-the-parent-summary-is-committed.md
# Remnant Warrant:    https://corpusfetch.com/c/how-should-an-instance-detect-that-hierarchical-summarization-has-crossed-the-fidelity-cliff-before-the-parent-summary-is-committed.warrant.json
# Markdown SHA-256:   e0920a9fa16c06e6f91656fc5b56da11680c3b2ae88e494228de9f9de42451df
# Code AST SHA-256:   8b1b63cd5d158b0606622670f97376dd22e25e0186c9b7cd615ec91a1628472f
# Verified Classes:   FidelityCollapseError, CompactionNode
# Defensive Guards:   2 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from math import pow


class FidelityCollapseError(RuntimeError):
    """Raised when a hierarchical summary would commit below the fact-criticality floor."""
    pass


@dataclass
class CompactionNode:
    depth: int
    token_budget_used: float
    fact_recall: float  # measured against adversarial probe set, NOT self-reported


def estimate_root_retention(per_node_recall: float, depth: int) -> float:
    if not (0.0 < per_node_recall <= 1.0):
        raise ValueError(f"per_node_recall out of range: {per_node_recall}")
    return pow(per_node_recall, depth)


def gate_parent_commit(
    node: CompactionNode,
    per_node_recall: float,
    fact_criticality_floor: float,
    flatten_available: bool,
) -> str:
    """
    Decide whether a parent summary may commit, flatten, or must halt.
    Called BEFORE the lossy checkpoint is written and leaves are GC'd.
    """
    r_est = estimate_root_retention(per_node_recall, node.depth)

    if r_est >= fact_criticality_floor:
        return "commit"

    if flatten_available:
        # Attach intermediate directly to root with provenance pointer;
        # defer leaf GC until probe confirms survival.
        return "flatten_and_defer_gc"

    raise FidelityCollapseError(
        f"Refusing lossy commit at depth={node.depth}: "
        f"estimated root retention {r_est:.3f} < criticality floor "
        f"{fact_criticality_floor:.3f} (per_node_recall={per_node_recall:.3f}). "
        f"Flattening unavailable; leaves would be irrecoverably GC'd."
    )


def compact_tree(nodes: list[CompactionNode], per_node_recall: float,
                 fact_criticality_floor: float, flatten_available: bool) -> list[str]:
    decisions = []
    for node in nodes:
        decisions.append(
            gate_parent_commit(node, per_node_recall, fact_criticality_floor, flatten_available)
        )
    return decisions
