# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-structural-signals-indicate-that-hierarchical-summarization-has-crossed-the-fidelity-budget-boundary-during-automatic-context-compaction.md
# Remnant Warrant:    https://corpusfetch.com/c/what-structural-signals-indicate-that-hierarchical-summarization-has-crossed-the-fidelity-budget-boundary-during-automatic-context-compaction.warrant.json
# Markdown SHA-256:   b3f47059d4de4c9371f66eb76560242747857b46b06c02dfabd12b589eccc3c9
# Code AST SHA-256:   513aa20c9cba604f58e7b74d42d2c8cf5065d13d061568984bd5426c465d1468
# Verified Classes:   FidelityBudgetExceeded, SummaryNode
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from dataclasses import dataclass, field


class FidelityBudgetExceeded(RuntimeError):
    """Raised when a parent summary node has crossed the fidelity boundary."""
    pass


@dataclass
class SummaryNode:
    text: str
    children: list = field(default_factory=list)
    provenance: float = 1.0          # fraction of sentences traceable to a child span
    orphan_density: float = 0.0      # undefined referents / total referents
    resolved_conflicts: int = 0      # sibling contradictions silently resolved


PROVENANCE_FLOOR = 0.50
PROVENANCE_WARN = 0.70
ORPHAN_CEILING = 0.30
ORPHAN_WARN = 0.45


def evaluate_promotion(node: SummaryNode) -> str:
    """Return 'promote', 'halt', or raise if the node is invalid.

    'halt' means: stop growing the hierarchy here, keep children as context.
    Raises FidelityBudgetExceeded when the parent must be discarded and rebuilt.
    """
    if node.resolved_conflicts > 0:
        raise FidelityBudgetExceeded(
            f"parent silently resolved {node.resolved_conflicts} sibling "
            f"contradiction(s); conflict markers lost, parent is not "
            f"traceable and must be rebuilt from children"
        )

    if node.provenance < PROVENANCE_FLOOR:
        raise FidelityBudgetExceeded(
            f"provenance {node.provenance:.2f} below floor "
            f"{PROVENANCE_FLOOR:.2f}; parent asserts unsupported content"
        )

    if node.provenance < PROVENANCE_WARN or node.orphan_density > ORPHAN_WARN:
        return "halt"

    if node.orphan_density > ORPHAN_CEILING:
        return "halt"

    return "promote"


def compact_level(parent: SummaryNode) -> None:
    decision = evaluate_promotion(parent)
    if decision == "halt":
        # Refuse to grow the tree further; retain children verbatim.
        raise FidelityBudgetExceeded(
            "warning band reached: halting hierarchy growth and retaining "
            "child nodes as working context rather than promoting parent"
        )
    # decision == "promote": parent is safe to use as sole context.
