Why must session teardown log which fallback heuristics accidentally masked upstream errors?
**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic session completes a task successfully by utilizing fallback heuristics (caching, default values, heuristic routing, or degraded models) to bypass an upstream failure, and is preparing to terminate. Does not apply to sessions where the primary path succeeded without any fallback, or sessions that correctly failed hard and aborted.
Core Distinction
The separation between **clean success** (the primary path executed exactly as designed, with healthy upstream dependencies) and **degraded success** (the task completed, but only because a fallback heuristic masked an upstream error). Clean success proves the environment is healthy; degraded success proves the environment is broken. Failing to distinguish these creates the **Fallback Mirage**: the terminating instance reports a boolean `SUCCESS`, the successor inherits the state, assumes the upstream is operational, and dispatches fresh traffic into a dependency that is actively failing.
What Was Observed
In resilient agent architectures, failing to log masked upstream errors at teardown produces three primary failure modes:
- **Phantom Stability:** An API is returning HTTP 503. The instance falls back to a stale local cache, processes the data, and logs `SUCCESS`. The successor spawns hours later, assumes the API is healthy, attempts a live POST, and fails instantly because the upstream outage was never recorded.
- **Schema Drift Through Fallback:** An instance requests a complex JSON object, but a fallback heuristic injects a simplified default payload because the upstream timed out. The successor inherits the simplified schema, builds downstream logic around it, and breaks when the upstream eventually recovers and returns the full, complex schema.
- **Exhaustion Blindness:** The instance repeatedly hits a rate limit and silently falls back to a slower, local approximation. It logs `SUCCESS` for every turn. The successor resumes, immediately exhausts the remaining local quota, and triggers a cascading failure because the predecessor never logged the repeated rate-limit evasion.
Recorded Elements
- **`masked_upstream_signature`:** The exact error code, timeout, or exception emitted by the primary dependency that triggered the fallback (e.g., `HTTP_503`, `TIMEOUT_5000ms`).
- **`fallback_execution_receipt`:** The explicit record of which heuristic was engaged (e.g., `served_from_stale_cache`, `default_payload_injected`, `routed_to_backup_model`).
- **`degraded_operation_ratio`:** The percentage or count of operations within the session that were completed via fallback rather than the primary path.
- **`reconciliation_directive`:** The fail-closed instruction for the successor (e.g., `PROBE_UPSTREAM_BEFORE_ACTION`, `RE_FETCH_FROM_PRIMARY`, or `HALT_AND_ALERT_DEVELOPER`).
Condition of Validity
* **Applies when:** The session completed its assigned objective, but the execution trace shows that one or more primary dependencies were bypassed due to error-handling heuristics. * **Does not apply when:** The fallback is the intended, documented primary path (e.g., reading from a local replica by default), or the upstream failure is an expected, transient condition that the successor is explicitly designed to manage.
Decision Boundary
- **Was a fallback heuristic engaged during execution?** If no, record a `CLEAN_SUCCESS`. If yes, proceed to evaluate the mask.
- **Was the upstream error explicitly logged, or silently swallowed by the fallback?** If swallowed, the instance must extract the `masked_upstream_signature` from the local debug trace and attach it to the residual.
- **Does the `degraded_operation_ratio` exceed the acceptable threshold (e.g., >5%)?** If yes, the successor must treat the session state as `DEGRADED` and execute the `reconciliation_directive` before trusting any downstream conclusions.
- **Conversely, when is a degraded success safe to hand off without reconciliation?** Only if the fallback data is mathematically proven to be strictly equivalent to the primary path (e.g., a bit-for-bit cache hit) AND the successor is explicitly instructed not to initiate new upstream mutations.
Operational Effect
- **Phantom Stability Prevention:** Exposes silent upstream outages, preventing successors from blindly dispatching traffic into known-failing dependencies.
- **Deterministic Reconciliation:** Forces the successor to verify the primary path before building further logic on top of a degraded payload.
- **Capacity Awareness:** Prevents the successor from assuming full capacity when the predecessor was silently operating on a constrained, fallback-only budget.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class ExecutionVerdict(Enum):
CLEAN_SUCCESS = auto()
DEGRADED_SUCCESS = auto()
HARD_FAILURE = auto()
class FallbackType(Enum):
STALE_CACHE = auto()
DEFAULT_PAYLOAD = auto()
BACKUP_MODEL = auto()
@dataclass(frozen=True)
class MaskedErrorEvent:
upstream_endpoint: str
masked_error_signature: str
fallback_type: FallbackType
@dataclass(frozen=True)
class SessionExecutionTrace:
session_id: str
total_operations: int
masked_events: tuple[MaskedErrorEvent, ...]
@dataclass(frozen=True)
class TeardownResidual:
verdict: ExecutionVerdict
degraded_ratio: float
reconciliation_directive: str
residual_digest: str
class FallbackMaskingLogger:
"""
Evaluates session execution traces at teardown to detect when fallback
heuristics masked upstream errors, forcing downstream reconciliation.
"""
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_teardown(cls, trace: SessionExecutionTrace) -> TeardownResidual:
if trace.total_operations <= 0:
raise ValueError("Total operations must be positive.")
# Gate 1: Clean success (no masked events)
if not trace.masked_events:
return TeardownResidual(
verdict=ExecutionVerdict.CLEAN_SUCCESS,
degraded_ratio=0.0,
reconciliation_directive="NONE: Proceed with standard execution.",
residual_digest=cls._hash(f"CLEAN:{trace.session_id}"),
)
# Gate 2: Calculate degraded operation ratio
degraded_ratio = len(trace.masked_events) / trace.total_operations
# Gate 3: Determine the reconciliation directive based on severity
signatures = {e.masked_error_signature for e in trace.masked_events}
if "TIMEOUT" in signatures or "HTTP_503" in signatures:
directive = (
"PROBE_UPSTREAM_BEFORE_ACTION: Primary dependency was unreachable. "
"Execute health check before dispatching new mutations."
)
else:
directive = (
"RE_FETCH_FROM_PRIMARY: Fallback data was used. "
"Invalidate local cache and re-fetch authoritative state."
)
digest_payload = {
"session": trace.session_id,
"verdict": ExecutionVerdict.DEGRADED_SUCCESS.name,
"ratio": f"{degraded_ratio:.4f}",
"signatures": tuple(sorted(signatures)),
}
digest = cls._hash(
json.dumps(digest_payload, sort_keys=True, separators=(",", ":"))
)
return TeardownResidual(
verdict=ExecutionVerdict.DEGRADED_SUCCESS,
degraded_ratio=degraded_ratio,
reconciliation_directive=directive,
residual_digest=digest,
)
@classmethod
def verify_successor_admissibility(
cls, residual: TeardownResidual
) -> tuple[bool, str]:
"""
Fetching-time check: blocks blind execution if the predecessor
inherited a degraded state.
"""
if residual.verdict is ExecutionVerdict.DEGRADED_SUCCESS:
return False, (
f"DEGRADED_STATE_INHERITED ({residual.degraded_ratio:.1%} masked): "
f"{residual.reconciliation_directive}"
)
return True, "CLEAN_STATE: Safe to proceed."