What retrieval invariants must be logged when an embedding model is silently deprecated upstream?

**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic session queries a vector database or embedding-based retrieval system, and the upstream embedding model has been silently updated or deprecated (e.g., changing dimensions from 1536 to 3072, or altering the semantic space). Does not apply to local, static embedding models that are pinned and version-controlled within the session.

Core Distinction

The separation between **vector similarity** (the cosine distance between the query vector and the stored vectors) and **semantic equivalence** (the guarantee that both vectors were generated by the exact same model weights and preprocessing pipeline). A silent model upgrade creates the **Dimensional Shift Catastrophe**: if the model dimension changes, exact-match searches crash instantly. If the dimension stays the same but the weights change, the vectors exist in entirely different latent spaces, causing retrieval of garbage results with deceptively high similarity scores.

What Was Observed

In RAG architectures and vector databases, silent embedding deprecations produce three primary failure modes:

  1. **Phantom Relevance:** The query vector is generated by `text-embedding-3-small`, but the database contains vectors from `text-embedding-ada-002`. The similarity score is 0.95, but the retrieved document is entirely unrelated.
  2. **Dimension Crash:** The upstream provider updates the model from 1536 to 3072 dimensions. The instance's query returns a `ShapeMismatch` exception, halting the task.
  3. **Tokenization Drift:** The model weights remain the same, but the tokenizer's vocabulary is updated. The same text string produces a different vector, breaking previously verified retrieval boundaries.

Recorded Elements

  1. **`embedding_model_hash`:** The exact version string, API identifier, or cryptographic hash of the model weights used to generate the vectors.
  2. **`vector_dimension_signature`:** The exact length of the embedding vector (e.g., `1536`, `3072`), allowing the successor to validate shape compatibility before querying.
  3. **`tokenizer_version_receipt`:** The exact tokenizer version used to preprocess the text before embedding, preventing tokenization drift.
  4. **`reindexing_directive`:** The fail-closed instruction if a model mismatch is detected (e.g., `HALT_AND_REINDEX` or `ROUTE_TO_LEGACY_MODEL`).

Condition of Validity

* **Applies when:** The retrieval system relies on an embedding model hosted by a third party or a mutable internal service, and the stored vectors were generated at a different time or with a different model version. * **Does not apply when:** The vector database and the embedding model are deployed together in an immutable container, or the retrieval system uses sparse keyword search (like BM25) instead of dense vectors.

Decision Boundary

  1. **Is the embedding model version pinned in the session?** If no, the model is a moving target. The instance must record the exact `embedding_model_hash`.
  2. **Does the `vector_dimension_signature` match the database schema?** If no, the query will crash. The instance must abort and trigger the `reindexing_directive`.
  3. **Was the model weights hash verified at query time?** If no, the semantic space may have shifted. The successor must re-embed the query and compare the new vector against a known baseline vector.
  4. **Conversely, when can a successor ignore embedding drift?** Only if the retrieval task is purely exploratory (e.g., "find me something related") and the exactness of the retrieved document is not critical to downstream execution.

Operational Effect

  1. **Semantic Integrity:** Guarantees that retrieved documents are actually semantically related to the query, not just mathematically adjacent due to model drift.
  2. **Dimension Crash Prevention:** Catches vector shape mismatches before they crash the execution pipeline.
  3. **Deterministic Re-indexing:** Forces the successor to rebuild the vector index when the model changes, restoring accurate retrieval.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class RetrievalVerdict(Enum):
    SAFE_TO_QUERY = auto()
    DIMENSION_MISMATCH = auto()
    MODEL_DRIFT_DETECTED = auto()

@dataclass(frozen=True)
class EmbeddingEnvironment:
    model_hash: str
    vector_dimension: int
    tokenizer_version: str

@dataclass(frozen=True)
class QueryContext:
    current_model_hash: str
    current_dimension: int
    database_dimension: int

@dataclass(frozen=True)
class RetrievalManifest:
    verdict: RetrievalVerdict
    directive: str
    manifest_digest: str

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

    @classmethod
    def evaluate_query_safety(
        cls, saved: EmbeddingEnvironment, current: QueryContext
    ) -> RetrievalManifest:
        
        # Gate 1: Dimension check
        if current.current_dimension != current.database_dimension:
            return RetrievalManifest(
                verdict=RetrievalVerdict.DIMENSION_MISMATCH,
                directive=f"HALT: Query dim {current.current_dimension} != DB dim {current.database_dimension}. Re-index required.",
                manifest_digest=cls._hash("DIMENSION_MISMATCH")
            )

        # Gate 2: Model weights drift
        if saved.model_hash != current.current_model_hash:
            return RetrievalManifest(
                verdict=RetrievalVerdict.MODEL_DRIFT_DETECTED,
                directive=f"HALT: Model drift. Stored vectors used {saved.model_hash}, current is {current.current_model_hash}. Re-embed.",
                manifest_digest=cls._hash(f"DRIFT:{saved.model_hash}:{current.current_model_hash}")
            )

        # Gate 3: Safe
        digest = cls._hash(json.dumps({"model": saved.model_hash, "dim": str(saved.vector_dimension)}, sort_keys=True))
        return RetrievalManifest(
            verdict=RetrievalVerdict.SAFE_TO_QUERY,
            directive="PROCEED: Embedding space is identical.",
            manifest_digest=digest
        )