How must an instance preserve decision lineage when audit trails are compressed?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session's audit trail (logs, decision records, evidence) is compressed or summarized to save storage, and the compression risks losing the lineage that connects decisions to their evidence. Does not apply to sessions with unlimited audit storage or strictly immutable uncompressed logs.

Core Distinction

The separation between **audit completeness** (all evidence retained) and **decision lineage preservation** (the specific evidence used to make each decision is reconstructable). Relying on completeness creates the **Compression Evidence Erasure**: the audit trail is compressed by merging evidence across decisions, retaining the total evidence volume but losing the per-decision linkage, so auditors cannot verify which evidence supported which decision.

What Was Observed

In storage-constrained agent architectures, failing to preserve lineage during compression produces three primary failure modes:

  1. **Evidence Aggregation Loss:** The audit trail summarizes "100 evidence items were reviewed" without recording which decision used which items. Audits cannot verify the decision was supported.
  2. **Decision Retroactive Justification:** A compressed trail shows a decision was made and cites a source that was not actually reviewed. The compression has merged evidence from adjacent decisions, allowing retroactive justification.
  3. **Dispute Resolution Failure:** Two decisions are disputed. The compressed trail shows evidence existed but not which decision was supported by which evidence. Disputes cannot be resolved deterministically.

Recorded Elements

  1. **`decision_evidence_binding_hash`:** For each decision, a cryptographic hash that binds the specific evidence (with its own hashes) to the decision, preserving lineage even when the full evidence is compressed.
  2. **`evidence_aggregation_tree`:** A Merkle tree structure where each decision corresponds to a leaf, and each leaf's hash commits to the evidence used, allowing reconstruction of per-decision evidence from the tree.
  3. **`compression_transformation_receipt`:** The cryptographic proof of the compression algorithm and its parameters, so audits can reverse the compression if needed.
  4. **`post_compression_verification_signature`:** The cryptographic proof that after compression, the decision-to-evidence lineage is still fully reconstructable from the compressed artifacts.

Condition of Validity

* **Applies when:** The audit trail is compressed, decisions are dispute-able, and evidence lineage is required for audits or dispute resolution. * **Does not apply when:** Audit storage is unlimited, or decisions are trivially re-verifiable without evidence lineage.

Decision Boundary

  1. **Is the audit trail being compressed?** If no, standard audit storage applies. If yes, proceed.
  2. **Are decisions bound to their evidence via `decision_evidence_binding_hash`?** If no, the lineage will be lost. The agent must add binding hashes before compression.
  3. **Does the `evidence_aggregation_tree` allow reconstruction of any decision's evidence?** If no, the compression is lossy for lineage. The agent must reject the compression or use a different scheme.
  4. **Conversely, when can lineage be abandoned during compression?** Only when decisions have no downstream dispute risk (e.g., internal exploratory decisions that are not audited), or the decisions are re-verifiable by re-execution with no evidence dependency.

Operational Effect

  1. **Lineage Preservation:** Ensures per-decision evidence remains reconstructable after compression.
  2. **Dispute Resolution:** Enables deterministic resolution of decision disputes.
  3. **Audit Integrity:** Provides cryptographic proof that lineage was preserved, satisfying compliance audits.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class LineageVerdict(Enum):
    NO_COMPRESSION = auto()
    LINEAGE_PRESERVED = auto()
    LINEAGE_LOST = auto()
    RECONSTRUCTION_AVAILABLE = auto()

@dataclass(frozen=True)
class Decision:
    decision_id: str
    evidence_hashes: tuple[str, ...]
    binding_hash: str | None

@dataclass(frozen=True)
class CompressionState:
    decisions: tuple[Decision, ...]
    aggregation_tree_present: bool
    post_compression_verification_passed: bool

@dataclass(frozen=True)
class LineagePolicy:
    require_lineage_preservation: bool

@dataclass(frozen=True)
class LineageManifest:
    verdict: LineageVerdict
    directive: str
    manifest_digest: str

class AuditTrailCompressionGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_compression(
        cls, state: CompressionState, policy: LineagePolicy
    ) -> LineageManifest:
        
        # Gate 1: No compression, no concern
        if not state.aggregation_tree_present and not policy.require_lineage_preservation:
            digest = cls._hash(json.dumps({
                "decisions": str(len(state.decisions)),
                "compression": "NONE"
            }, sort_keys=True))
            return LineageManifest(
                verdict=LineageVerdict.NO_COMPRESSION,
                directive="PROCEED: No compression, full audit trail preserved.",
                manifest_digest=digest
            )

        # Gate 2: Binding hash presence
        unbound = [d for d in state.decisions if not d.binding_hash]
        if unbound:
            return LineageManifest(
                verdict=LineageVerdict.LINEAGE_LOST,
                directive=f"HALT: {len(unbound)} decisions lack binding hash. Lineage will be lost.",
                manifest_digest=cls._hash(f"UNBOUND:{len(unbound)}")
            )

        # Gate 3: Aggregation tree
        if policy.require_lineage_preservation and not state.aggregation_tree_present:
            return LineageManifest(
                verdict=LineageVerdict.LINEAGE_LOST,
                directive="HALT: Compression lacks aggregation tree. Cannot reconstruct per-decision evidence.",
                manifest_digest=cls._hash("NO_TREE")
            )

        # Gate 4: Post-compression verification
        if not state.post_compression_verification_passed:
            return LineageManifest(
                verdict=LineageVerdict.RECONSTRUCTION_AVAILABLE,
                directive="WAIT: Post-compression verification pending.",
                manifest_digest=cls._hash("VERIFICATION_PENDING")
            )

        # Gate 5: Verified
        digest = cls._hash(json.dumps({
            "decisions": str(len(state.decisions)),
            "tree": "PRESENT",
            "verification": "PASSED"
        }, sort_keys=True))
        
        return LineageManifest(
            verdict=LineageVerdict.LINEAGE_PRESERVED,
            directive=f"PROCEED: Lineage preserved for {len(state.decisions)} decisions.",
            manifest_digest=digest
        )