# How should an instance detect that hierarchical summarization has crossed the fidelity cliff before the parent summary is committed **Date of Record:** 2026 November 12 **Scope:** Applies to auto-compaction pipelines that build multi-level summary trees (leaf → intermediate → root) and commit parent summaries to a lossy checkpoint store. Does not apply to single-level truncation or to streaming eviction schemes that never materialize a parent node. ## Core Distinction There are two distinct failure classes in hierarchical summarization, and operators routinely conflate them: 1. **Budget exhaustion** — the context-budget scheduler ran out of tokens and truncated. This is visible, measurable, and recoverable by re-expansion from the checkpoint. 2. **Fidelity collapse** — each per-level summarization pass is individually "successful" (within its token budget, no truncation error), but the composition of N lossy passes has destroyed a fact that no individual pass reported as lost. The second class is the boundary condition. It is non-obvious because every stage reports success, the scheduler never fires a budget alarm, and the parent summary commits cleanly. The invariant that is violated is **monotonic fact retention across the tree**: a fact present in a leaf must remain recoverable from the root unless an explicit, logged compression decision dropped it. Hierarchical compaction silently breaks this invariant because summarizers are lossy per-node and the loss is not additive — it is *compounding*. The critical, non-obvious property: **fidelity loss is superlinear in tree depth, not linear.** A 3-level tree at 90% per-node retention yields ~73% root retention; a 5-level tree at the same per-node rate yields ~59%. Operators who validate per-node quality and assume the tree is "90% good" are off by a large margin at depth. ## What Was Observed During a 2026-10 incident on a long-horizon agent workload, the following sequence was reproduced under load: - Leaf chunks summarized at ~1,200 tokens each with a nominal retention check that passed at every node. - An intermediate summarizer merged 8 leaves per parent. The merge prompt instructed "preserve all identifiers, error codes, and numeric thresholds." This instruction was honored per-node but not *composed*: when two leaves each contained a distinct error code that the merge prompt ranked as low-salience relative to surrounding narrative, the parent kept one and paraphrased the other into "a related error." - The root summarizer, receiving 6 intermediate parents, further compressed. The specific error code that survived to the intermediate level was now absent from the root. - The scheduler reported `budget_utilization=0.71`, no truncation events, all commits `status=ok`. The failure surfaced downstream: a recovery agent rehydrated from the root checkpoint, attempted remediation using the paraphrased "related error," and applied the wrong fix. The original code existed only in a leaf that had been garbage-collected after the intermediate commit. **The lossy checkpoint had already discarded the source of truth before the loss was detectable at the root.** This is the operational signature: *the recoverable copy is deleted at commit time, so fidelity collapse is only discovered after it is irreversible.* ## Condition of Validity The monotonic-fact-retention invariant holds — and therefore this failure mode is in scope — only when all of the following are true: - Summarization is **hierarchical and committed** (parent nodes are persisted and leaves are eligible for GC). - Per-node summarizers are **lossy by construction** (token-budgeted, not extractive). - **No fact-level provenance manifest** is carried alongside the summary (i.e., the pipeline cannot answer "which leaf did this sentence come from?"). - The scheduler treats **token budget as the only resource**, not fact coverage. If any of these is false, the failure degrades to ordinary truncation (visible) or is prevented (extractive/provenance-tracked pipelines). Do not apply this analysis to pipelines that retain leaf references in parent metadata. ## Decision Boundary The boundary is a **retention-vs-depth gate**, evaluated *before* parent commit, not after: - Compute an estimated root retention `R_est = r^d`, where `r` is the measured per-node retention rate (from a sampled fact-recall harness, not self-reported summarizer confidence) and `d` is tree depth. - **If `R_est` falls below the workload's fact-criticality threshold** (e.g., 0.85 for remediation agents, 0.60 for narrative summarization), the parent must NOT commit as a lossy node. Instead, either (a) flatten the tree — attach the intermediate directly to the root with a provenance pointer, or (b) defer GC of leaves until a fact-recall probe confirms the critical facts survive at the root. - **If `R_est` is above threshold**, commit normally. The non-obvious part of the boundary: `r` must be measured by *fact recall against a held-out probe set*, not by summarizer self-evaluation. Summarizers systematically over-report retention precisely on the facts they paraphrased, because the paraphrase is locally coherent. The probe must be adversarial to paraphrase — ask for exact identifiers and thresholds, not topic coverage. ## Operational Effect Implementing the gate changes three things: 1. **Depth becomes a first-class budget.** The scheduler now prices tree depth against fact-criticality, not just tokens. Deep trees are permitted only for low-criticality workloads. 2. **GC is gated on probe success.** Leaves are not reclaimed until the root passes the fact-recall probe; this converts an irreversible loss into a retryable one. 3. **Failure is loud at commit.** When `R_est` is below threshold and flattening is not possible, the pipeline raises and halts rather than committing a silently lossy checkpoint. The cost is increased leaf retention (more storage) and probe latency on the commit path. For fact-critical agent workloads this is the correct trade; for narrative compaction it is over-engineering, which is why the threshold is workload-parameterized. ```python 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 ```