How should an instance discover root causes when observability data is intentionally sparse?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session is debugging an incident, but the observability data (logs, metrics, traces) is intentionally sparse due to cost constraints, sampling, or privacy redaction, and must reconstruct root cause from limited signals. Does not apply to fully instrumented systems or incidents with no observable signals.

Core Distinction

The separation between **absence of evidence** (the log line you wanted is not there) and **evidence of absence** (the system is provably healthy in the observed dimension). Relying on absence-of-evidence creates the **Sparse Observability Trap**: the agent sees no errors in the sampled logs, assumes the system is healthy, but the errors are occurring in the unsampled 99% of traffic. Root cause discovery requires distinguishing "not sampled" from "sampled and clean."

What Was Observed

In cost-constrained observability architectures, failing to account for sparse data produces three primary failure modes:

  1. **Silent Sampling Bias:** The agent samples only successful requests to save costs. When an incident occurs, there are no error logs, and the agent incorrectly concludes the system is healthy.
  2. **Dead-Man's Switch Absence:** The agent relies on the absence of alerts to conclude health. But the alerting system itself is down, so the absence of alerts means nothing.
  3. **Redaction Blindness:** PII redaction removes all user-identifying data from logs. During an incident, the agent cannot correlate which users are affected because the identifiers are gone.

Recorded Elements

  1. **`sampling_rate_signature`:** The exact sampling rate and sampling strategy (random, systematic, or biased) at the time of the incident, allowing the successor to estimate the probability that the absence of evidence is meaningful.
  2. **`observability_health_check_receipt`:** The cryptographic proof that the observability pipeline itself (log shipper, aggregator, alerting) is healthy, ensuring the absence of data is not due to pipeline failure.
  3. **`redaction_boundary_receipt`:** The exact fields that were redacted and the redaction algorithm used, allowing the successor to know what data is permanently unavailable.
  4. **`active_probe_results`:** For each observability gap, the exact result of an active probe (e.g., a synthetic request) that the agent executed to verify health in the absence of passive telemetry.

Condition of Validity

* **Applies when:** Observability data is sparse due to intentional sampling, cost, or privacy, and the agent must make decisions based on that sparse data. * **Does not apply when:** Observability is complete (100% sampling) and no redaction has occurred, or the agent has no observability data at all (in which case the decision should be made on a different basis).

Decision Boundary

  1. **Is the observability data complete?** If yes, standard root cause analysis applies. If no, proceed.
  2. **Is the `observability_health_check_receipt` valid?** If no, the absence of data may be due to pipeline failure. The agent must verify pipeline health before drawing conclusions.
  3. **Is the absence of a signal consistent with the `sampling_rate_signature`?** If yes, the absence may be statistically meaningful (assuming normal sampling). If no, the absence is suspicious.
  4. **Conversely, when can an agent conclude health from absence of evidence?** Only when the sampling rate is high enough that the incident would have been observed with high probability, AND the observability pipeline is verified healthy.

Operational Effect

  1. **Sparse Data Awareness:** Forces the agent to account for sampling rates and redaction, preventing false conclusions from sparse data.
  2. **Active Verification:** Requires active probes when passive telemetry is sparse, ensuring health is confirmed rather than assumed.
  3. **Confidence Quantification:** Enables the agent to quantify confidence in root cause conclusions based on the sparsity of the underlying data.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class RootCauseVerdict(Enum):
    HEALTH_CONFIRMED = auto()
    INCONCLUSIVE_SPARSE = auto()
    PIPELINE_UNHEALTHY = auto()

@dataclass(frozen=True)
class ObservabilityContext:
    sampling_rate: float
    pipeline_healthy: bool
    redaction_applied: bool
    active_probe_passed: bool
    incident_detection_probability: float

@dataclass(frozen=True)
class RootCausePolicy:
    min_confidence_threshold: float

@dataclass(frozen=True)
class RootCauseManifest:
    verdict: RootCauseVerdict
    confidence: float
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_absence(
        cls, ctx: ObservabilityContext, policy: RootCausePolicy
    ) -> RootCauseManifest:
        
        # Gate 1: Pipeline health
        if not ctx.pipeline_healthy:
            return RootCauseManifest(
                verdict=RootCauseVerdict.PIPELINE_UNHEALTHY,
                confidence=0.0,
                directive="HALT: Observability pipeline unhealthy. Absence of evidence is not evidence.",
                manifest_digest=cls._hash("PIPELINE_DOWN")
            )

        # Gate 2: Confidence from sampling rate
        confidence = ctx.incident_detection_probability

        # Gate 3: Active probe required for sparse data
        if ctx.sampling_rate < 1.0 and not ctx.active_probe_passed:
            return RootCauseManifest(
                verdict=RootCauseVerdict.INCONCLUSIVE_SPARSE,
                confidence=confidence,
                directive=f"WARN: Sparse sampling ({ctx.sampling_rate:.2%}). Active probe required before concluding health.",
                manifest_digest=cls._hash(f"SPARSE:{ctx.sampling_rate:.3f}")
            )

        # Gate 4: Confidence threshold
        if confidence < policy.min_confidence_threshold:
            return RootCauseManifest(
                verdict=RootCauseVerdict.INCONCLUSIVE_SPARSE,
                confidence=confidence,
                directive=f"WARN: Confidence {confidence:.2f} < threshold {policy.min_confidence_threshold:.2f}.",
                manifest_digest=cls._hash(f"LOW_CONF:{confidence:.3f}")
            )

        # Gate 5: Healthy
        digest = cls._hash(json.dumps({
            "sample": f"{ctx.sampling_rate:.3f}",
            "confidence": f"{confidence:.3f}",
            "probe": str(ctx.active_probe_passed)
        }, sort_keys=True))
        
        return RootCauseManifest(
            verdict=RootCauseVerdict.HEALTH_CONFIRMED,
            confidence=confidence,
            directive=f"PROCEED: Health confirmed with confidence {confidence:.2f}.",
            manifest_digest=digest
        )