What memory invariants must survive a fleet-wide model update rollout in multiple phases?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agent fleet is undergoing a phased model update (canary rollout, blue-green deployment, or ring-based rollout), and agents operating on different model versions must exchange memory without incompatibility. Does not apply to atomic fleet-wide model swaps or fleets where all agents are guaranteed identical model versions.

Core Distinction

The separation between **atomic model rollout** (all agents update simultaneously to a single model version) and **phased rollout** (agents update in waves, creating a mixed-version fleet for the rollout duration). Relying on atomic-only semantics creates the **Mixed-Version Incompatibility**: Agent A on model v1 shares memory with Agent B on model v2, and either the memory format, semantic interpretation, or behavioral assumptions are incompatible, causing silent corruption or misinterpretation across the fleet.

What Was Observed

In fleet-wide model update architectures, failing to handle mixed-version states produces three primary failure modes:

  1. **Format Incompatibility:** Model v2 emits embeddings of a different dimension than v1. Agent A writes v1 embeddings, Agent B reads them assuming v2 dimensions, and retrieval fails silently.
  2. **Behavioral Assumption Break:** Model v1 assumed certain tool outputs, but model v2 changes the expected output format. Mixed-version agents exchange memory that assumes different tool behaviors, causing cascading errors.
  3. **Gradual Divergence:** During rollout, the fleet's shared memory accumulates a mixture of v1 and v2 records. Post-rollout, the fleet cannot distinguish which records are stale, and the memory becomes a permanent mixed-version artifact.

Recorded Elements

  1. **`model_version_signature`:** Every memory record is tagged with the exact model version that produced it, allowing version-aware reads.
  2. **`rollout_phase_epoch`:** The exact epoch of each rollout phase (canary, ring-1, ring-2, full), allowing the memory layer to know which model versions are expected to be active.
  3. **`cross_version_compatibility_receipt`:** For records that are intentionally shared across versions, a cryptographic proof of compatibility (e.g., embeddings converted to a canonical form, tool outputs normalized).
  4. **`version_migration_manifest`:** For records that need migration post-rollout (e.g., re-embedding v1 records with v2), the exact migration protocol and completion receipts.

Condition of Validity

* **Applies when:** The model update is phased (not atomic), the fleet shares memory across model versions, and version incompatibility is possible. * **Does not apply when:** The model update is atomic (all agents update simultaneously), or the fleet's shared memory is strictly isolated per model version.

Decision Boundary

  1. **Is the model update atomic or phased?** If atomic, standard memory management applies. If phased, proceed.
  2. **Is every memory record tagged with a `model_version_signature`?** If no, mixed-version records cannot be distinguished. The fleet must halt writes until tagging is enforced.
  3. **Are cross-version records verified by `cross_version_compatibility_receipt`?** If no, they cannot be safely exchanged across versions. Reads must be version-filtered.
  4. **Conversely, when can a fleet accept mixed-version records without verification?** Only when the model versions are byte-identical (no change) or the records are strictly version-agnostic (e.g., opaque blobs).

Operational Effect

  1. **Format Incompatibility Prevention:** Prevents v1 and v2 embeddings from being silently mixed.
  2. **Behavioral Assumption Safety:** Enforces that memory exchanges respect each model's assumptions.
  3. **Clean Migration:** Provides deterministic migration protocols for post-rollout normalization.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class RolloutVerdict(Enum):
    ATOMIC_SAFE = auto()
    MIXED_VERSION_FILTERED = auto()
    MIXED_VERSION_UNSAFE = auto()
    CROSS_VERSION_COMPATIBLE = auto()

@dataclass(frozen=True)
class MemoryRecord:
    record_id: str
    model_version_signature: str
    compatibility_receipt: str | None

@dataclass(frozen=True)
class RolloutContext:
    current_phase: str
    active_model_versions: frozenset[str]
    reader_model_version: str
    rollout_is_atomic: bool

@dataclass(frozen=True)
class RolloutManifest:
    verdict: RolloutVerdict
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_record(
        cls, record: MemoryRecord, ctx: RolloutContext
    ) -> RolloutManifest:
        
        # Gate 1: Atomic rollout
        if ctx.rollout_is_atomic:
            digest = cls._hash(json.dumps({
                "record": record.record_id,
                "version": record.model_version_signature,
                "rollout": "ATOMIC"
            }, sort_keys=True))
            return RolloutManifest(
                verdict=RolloutVerdict.ATOMIC_SAFE,
                directive="PROCEED: Atomic rollout, no version mismatch possible.",
                manifest_digest=digest
            )

        # Gate 2: Same version
        if record.model_version_signature == ctx.reader_model_version:
            digest = cls._hash(json.dumps({
                "record": record.record_id,
                "version": record.model_version_signature,
                "phase": ctx.current_phase
            }, sort_keys=True))
            return RolloutManifest(
                verdict=RolloutVerdict.ATOMIC_SAFE,
                directive=f"PROCEED: Record matches reader version {ctx.reader_model_version}.",
                manifest_digest=digest
            )

        # Gate 3: Cross-version compatibility
        if record.compatibility_receipt:
            digest = cls._hash(json.dumps({
                "record": record.record_id,
                "producer": record.model_version_signature,
                "reader": ctx.reader_model_version,
                "compatibility": record.compatibility_receipt
            }, sort_keys=True))
            return RolloutManifest(
                verdict=RolloutVerdict.CROSS_VERSION_COMPATIBLE,
                directive=f"PROCEED: Cross-version record verified compatible.",
                manifest_digest=digest
            )

        # Gate 4: Unsafe cross-version
        if record.model_version_signature not in ctx.active_model_versions:
            return RolloutManifest(
                verdict=RolloutVerdict.MIXED_VERSION_UNSAFE,
                directive=f"HALT: Record from inactive version {record.model_version_signature}.",
                manifest_digest=cls._hash(f"INACTIVE:{record.model_version_signature}")
            )

        return RolloutManifest(
            verdict=RolloutVerdict.MIXED_VERSION_FILTERED,
            directive=f"FILTER: Cross-version record without compatibility receipt. Reader must filter.",
            manifest_digest=cls._hash(f"UNVERIFIED:{record.record_id}")
        )