What multicast fan-out invariants must survive heterogeneous receiver capabilities?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session sends a message to multiple peers (multicast, broadcast, or fan-out), and the receivers have heterogeneous capabilities (different protocol versions, different buffer sizes, different processing speeds). Does not apply to strictly point-to-point messaging or fan-out to homogeneous receivers.
Core Distinction
The separation between **fan-out transmission** (the message is sent to all receivers) and **fan-out delivery** (each receiver successfully processes the message). Relying on transmission creates the **Heterogeneous Fanout Degradation**: receivers with weaker capabilities cannot process the message at the speed or format the sender provided, causing some to fail silently while others succeed, and the sender has no visibility into which receivers actually got the message.
What Was Observed
In heterogeneous fan-out architectures, failing to account for receiver capability variance produces three primary failure modes:
- **Buffer Overflow Slaughter:** The sender fans out to 100 receivers. 20 have small buffers and cannot accept the full message. Their transports drop the message silently, and the sender thinks all 100 received.
- **Protocol Version Rejection:** The message is sent in v2 format. Half the receivers only support v1. Those receivers reject the message, but the sender has no way to know unless it queries.
- **Slow Receiver Dominance:** If the sender waits for the slowest receiver to acknowledge, one slow receiver (with 100x the processing time) blocks the entire fan-out, and fast receivers have already moved on.
Recorded Elements
- **`receiver_capability_registry_receipt`:** For each receiver, its supported protocol versions, buffer size, and typical processing speed, so the sender knows what each can handle.
- **`per_receiver_delivery_receipt`:** For each receiver, a cryptographic acknowledgment of successful processing (or failure reason), so the sender can track actual delivery, not just transmission.
- **`capability_aware_encoding_signature`:** The encoding or format chosen based on the receiver set (e.g., "use v1 format when any receiver requires v1," "chunk messages when any receiver has small buffers").
- **`fanout_completion_threshold_signature`:** The exact threshold for considering the fan-out complete (all receivers, majority of receivers, or all critical receivers), preventing one slow receiver from blocking.
Condition of Validity
* **Applies when:** Messages are sent to multiple receivers, receiver capabilities vary, and sender needs delivery certainty. * **Does not apply when:** Receivers are homogeneous, or fan-out is strictly best-effort (no delivery certainty needed).
Decision Boundary
- **Is the fan-out to heterogeneous receivers?** If no, standard multicast applies. If yes, proceed.
- **Is the `receiver_capability_registry_receipt` current?** If yes, use it to choose encoding and chunking. If no, assume worst-case capabilities.
- **Is `capability_aware_encoding_signature` applied?** If yes, encoding matches receivers. If no, the message may fail for some receivers.
- **Conversely, when can a sender skip capability awareness?** Only when the message is strictly format-agnostic (receivers handle any format), or all receivers are verified to support the sender's default format.
Operational Effect
- **Delivery Certainty:** Per-receiver receipts distinguish transmission from delivery.
- **Capability Matching:** Encoding adapts to receiver capabilities.
- **Slow Receiver Isolation:** Completion threshold prevents one slow receiver from blocking the fan-out.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class FanoutVerdict(Enum):
HOMOGENEOUS = auto()
CAPABILITY_AWARE_FANOUT = auto()
ENCODING_MISMATCH = auto()
DELIVERY_INCOMPLETE = auto()
@dataclass(frozen=True)
class Receiver:
receiver_id: str
supported_versions: frozenset[str]
buffer_size_bytes: int
typical_processing_ms: float
is_critical: bool
@dataclass(frozen=True)
class FanoutMessage:
message_id: str
size_bytes: int
sender_version: str
receivers: tuple[Receiver, ...]
acknowledgment_rate: float
@dataclass(frozen=True)
class FanoutPolicy:
min_acknowledgment_rate: float
require_critical_acks: bool
@dataclass(frozen=True)
class FanoutManifest:
verdict: FanoutVerdict
directive: str
manifest_digest: str
class MulticastHeterogeneousGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_fanout(
cls, msg: FanoutMessage, policy: FanoutPolicy
) -> FanoutManifest:
if not msg.receivers:
return FanoutManifest(
verdict=FanoutVerdict.DELIVERY_INCOMPLETE,
directive="HALT: No receivers.",
manifest_digest=cls._hash("NO_RECEIVERS")
)
# Gate 1: Homogeneous check
version_sets = {r.supported_versions for r in msg.receivers}
buffer_sizes = {r.buffer_size_bytes for r in msg.receivers}
if len(version_sets) == 1 and len(buffer_sizes) == 1:
digest = cls._hash(json.dumps({
"message": msg.message_id,
"version": msg.sender_version
}, sort_keys=True))
return FanoutManifest(
verdict=FanoutVerdict.HOMOGENEOUS,
directive=f"PROCEED: Homogeneous receivers, standard multicast.",
manifest_digest=digest
)
# Gate 2: Version compatibility
incompatible = [r.receiver_id for r in msg.receivers if msg.sender_version not in r.supported_versions]
if incompatible:
return FanoutManifest(
verdict=FanoutVerdict.ENCODING_MISMATCH,
directive=f"HALT: {len(incompatible)} receivers do not support {msg.sender_version}.",
manifest_digest=cls._hash(f"VERSION_MISMATCH:{len(incompatible)}")
)
# Gate 3: Buffer size check
undersized = [r.receiver_id for r in msg.receivers if r.buffer_size_bytes < msg.size_bytes]
if undersized:
return FanoutManifest(
verdict=FanoutVerdict.ENCODING_MISMATCH,
directive=f"ACTION: {len(undersized)} receivers have smaller buffers than message size. Chunk required.",
manifest_digest=cls._hash(f"SMALL_BUFFER:{len(undersized)}")
)
# Gate 4: Acknowledgment
if msg.acknowledgment_rate < policy.min_acknowledgment_rate:
return FanoutManifest(
verdict=FanoutVerdict.DELIVERY_INCOMPLETE,
directive=f"HALT: Ack rate {msg.acknowledgment_rate:.2%} < {policy.min_acknowledgment_rate:.2%}.",
manifest_digest=cls._hash(f"LOW_ACK:{msg.acknowledgment_rate:.3f}")
)
# Gate 5: Capability-aware fanout
digest = cls._hash(json.dumps({
"message": msg.message_id,
"receivers": tuple(sorted(r.receiver_id for r in msg.receivers)),
"versions": tuple(sorted(str(v) for v in version_sets)),
"ack_rate": f"{msg.acknowledgment_rate:.3f}"
}, sort_keys=True))
return FanoutManifest(
verdict=FanoutVerdict.CAPABILITY_AWARE_FANOUT,
directive=f"PROCEED: {len(msg.receivers)} receivers with varied capabilities, {msg.acknowledgment_rate:.1%} ack.",
manifest_digest=digest
)