How must an instance prevent resource exhaustion cascades across peer agents?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session's resource exhaustion (compute, memory, quota, or tokens) can trigger cascading exhaustion in peer agents that depend on the session or share its resources, requiring the session to fail in ways that bound cascade rather than amplifying it. Does not apply to sessions with strictly isolated resources or sessions with no peer dependencies.
Core Distinction
The separation between **individual resource exhaustion** (one session runs out of a resource) and **cascade resource exhaustion** (one session's exhaustion propagates to peers, causing them to fail or degrade). Relying on individual handling creates the **Exhaustion Amplification**: the session's normal failure behavior (retries, requests to peers for help) actually consumes more resources, accelerating the cascade and taking down more of the network.
What Was Observed
In resource-interdependent agent networks, failing to prevent cascades produces three primary failure modes:
- **Retry Storm:** Session A exhausts its quota. Its retry logic retries aggressively against Session B's services, and B exhausts under the amplified load. B's retries then hit C, and the cascade spreads.
- **Peer Fallback Overload:** Session A exhausts its quota and requests fallback from Session B. B serves A, B's own quota is consumed, B's successors need fallback, and the fallback system becomes the cascade's transmission vector.
- **Shared Resource Contention:** Sessions A, B, and C share a resource pool. A starts consuming heavily (approaching exhaustion), and B and C consume defensively to "reserve" their share. All three exhaust simultaneously, faster than any would alone.
Recorded Elements
- **`cascade_aware_failure_receipt`:** For each failure or exhaustion event, a cryptographic proof that the failure is handled in a cascade-safe way (backoff with jitter, no peer fallback requests, or explicit resource surrender).
- **`peer_dependency_graph_signature`:** The dependency graph showing which peers depend on the session's resources, so the session knows which peers would be affected by its exhaustion.
- **`cascade_containment_policy_receipt`:** The explicit policy for cascade containment (e.g., "isolate failed sessions immediately," "no peer fallback when exhaustion rate is high," "surrender resources before reaching depletion").
- **`shared_resource_quota_signature`:** For shared pools, the exact allocation per session and the enforcement mechanism, so no single session can cause cascade through over-consumption.
Condition of Validity
* **Applies when:** Resources are interdependent across agents, exhaustion can propagate, and cascade amplification has material consequences. * **Does not apply when:** Resources are strictly isolated, or exhaustion is limited to a single session.
Decision Boundary
- **Are the session's resources shared with or depended on by peers?** If no, standard failure handling applies. If yes, proceed.
- **Is the `peer_dependency_graph_signature` mapped?** If yes, the session knows its cascade risk. If no, the session must assume any failure cascades.
- **Does the `cascade_containment_policy_receipt` specify cascade-safe failure behavior?** If yes, follow it. If no, default to isolation (halt the session cleanly rather than retrying or requesting fallback).
- **Conversely, when can a session request peer fallback while exhausting?** Only when the peer has verified spare capacity AND the peer is not itself at risk of cascade AND the fallback is bounded in scope (single retry, not unbounded retries).
Operational Effect
- **Retry Storm Prevention:** Cascade-safe failure replaces retry amplification.
- **Fallback Discipline:** Peer fallback is used only when it cannot amplify the cascade.
- **Shared Pool Protection:** Shared resources have enforced quotas that prevent cascade through contention.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class CascadeVerdict(Enum):
ISOLATED_RESOURCES = auto()
CASCADE_SAFE_FAILURE = auto()
CASCADE_AMPLIFICATION_RISK = auto()
SHARED_POOL_AT_RISK = auto()
@dataclass(frozen=True)
class ResourceState:
resource_id: str
utilization: float # 0.0 - 1.0
is_shared: bool
dependent_peers: tuple[str, ...]
@dataclass(frozen=True)
class FailureProposal:
failure_id: str
action: str # "RETRY" | "PEER_FALLBACK" | "HALT" | "SURRENDER"
retry_count: int
fallback_scope: str | None
@dataclass(frozen=True)
class CascadePolicy:
max_retry_before_halt: int
require_spare_capacity_for_fallback: bool
@dataclass(frozen=True)
class CascadeManifest:
verdict: CascadeVerdict
directive: str
manifest_digest: str
class ResourceCascadeGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_failure(
cls, state: ResourceState, failure: FailureProposal, policy: CascadePolicy
) -> CascadeManifest:
# Gate 1: Isolated
if not state.is_shared and not state.dependent_peers:
digest = cls._hash(json.dumps({
"resource": state.resource_id,
"failure": failure.failure_id,
"isolation": "TRUE"
}, sort_keys=True))
return CascadeManifest(
verdict=CascadeVerdict.ISOLATED_RESOURCES,
directive=f"PROCEED: Isolated resources, standard failure handling.",
manifest_digest=digest
)
# Gate 2: Retry amplification
if failure.action == "RETRY" and failure.retry_count > policy.max_retry_before_halt:
return CascadeManifest(
verdict=CascadeVerdict.CASCADE_AMPLIFICATION_RISK,
directive=f"HALT: {failure.retry_count} retries > {policy.max_retry_before_halt}, cascade amplification.",
manifest_digest=cls._hash(f"RETRY_AMPLIFY:{failure.failure_id}")
)
# Gate 3: Peer fallback
if failure.action == "PEER_FALLBACK":
if not state.dependent_peers:
return CascadeManifest(
verdict=CascadeVerdict.CASCADE_AMPLIFICATION_RISK,
directive=f"HALT: Peer fallback requested but no dependent peers identified.",
manifest_digest=cls._hash(f"NO_PEERS:{failure.failure_id}")
)
# Check if utilization is high enough to threaten cascade
if state.utilization > 0.8:
return CascadeManifest(
verdict=CascadeVerdict.CASCADE_AMPLIFICATION_RISK,
directive=f"HALT: Util {state.utilization:.2f} > 0.8, fallback would cascade.",
manifest_digest=cls._hash(f"FALLBACK_CASCADE:{failure.failure_id}")
)
# Gate 4: Shared pool contention
if state.is_shared and state.utilization > 0.9:
return CascadeManifest(
verdict=CascadeVerdict.SHARED_POOL_AT_RISK,
directive=f"HALT: Shared pool at {state.utilization:.2f} utilization, contention risk.",
manifest_digest=cls._hash(f"POOL_CONTENTION:{state.resource_id}")
)
# Gate 5: Cascade-safe
digest = cls._hash(json.dumps({
"resource": state.resource_id,
"failure": failure.failure_id,
"action": failure.action,
"utilization": f"{state.utilization:.3f}",
"peers": state.dependent_peers
}, sort_keys=True))
return CascadeManifest(
verdict=CascadeVerdict.CASCADE_SAFE_FAILURE,
directive=f"PROCEED: {failure.action} is cascade-safe at utilization {state.utilization:.2f}.",
manifest_digest=digest
)