Why a gate

The problem was never the transform. Every embedding migration has two parts: computing a mapping between two embedding spaces, and deciding whether that mapping is good enough to ship. The first is a solved regression problem. The second is the part that decides whether your migration from re-embedding actually holds up — and it is the part most tooling skips.

Computing a mapping between two embedding spaces is easy. Embed the same ~2K calibration texts with both models, fit a ridge regression, and you have a transform. On BEIR SciFact, Isotrieve's default ridge mapping retains 86–92% of retrieval quality versus full re-embedding. Any library can fit that matrix.

The risk is not in the matrix — it is in retention: whether the mapped vectors still retrieve the right neighbors on your corpus, your queries, your tolerance for silent degradation. That is a validation problem, and no transform, however clever, answers it for you.

Failure modes a transform can't see

These are exactly the failures that are invisible at fit time but show up in production. A mapping library reports a fitted loss; none of these show up in that number.

  • Hubness — mapped vectors can crowd around dense regions, so a handful of documents become near-neighbors to almost every query. The map fits a global regression; it cannot see per-vector crowding.
  • Seed sensitivity — different calibration seeds preserve different neighborhoods. One seed can look clean and another degrades, so a single fitted run is not evidence of quality.
  • Domain mismatch — pretrained adapters are fit on general data. On your distribution, the same transform can silently lose relevance with no error raised.

Isotrieve is Migration CI: it measures retention on held-out source/target samples and returns a verdict — PASS, WARN, or FAIL — so you decide to ship based on data, not hope. If you are planning to migrate an embedding model, this is the part that turns the project from a gamble into a reviewed change.

Adapter library vs Isotrive

Honestly: adapter libraries are good at what they do. They translate between embedding spaces using pretrained adapters for popular model pairs. They solve a different problem.

Adapter librariesIsotrive
Mapping sourcePretrained on general dataFit on your corpus
Where vectors liveTranslated at query timeMigrated in place, in your store
Failure modeYou find out in productionGate fails the build
Domain corporaGeneral-purpose fitCalibrated to your distribution

Use an adapter library when you want to query an existing index with a different model today — a small store, a short-lived experiment, an A/B test, or a prototype that may never go to production. Query-time translation is the right tool when the work is transient.

Use Isotrive when the migration is permanent: you want to move the index itself into the new space, in place, and be able to prove it did not degrade. That is the gap the gate fills — adapter libraries hand you a transform and wish you luck; Isotrive tells you whether to ship it.

Bring your own transform

"Bring your own transform — the gate decides." You are not locked into an Isotrive-fitted map. If you already have a transform from another tool, the gate still works on it.

ExternalMapping wraps any callable (np.ndarray) -> np.ndarray — including pretrained adapters from libraries like EmbeddingAdapters — so you can gate a transform you never fitted yourself. Isotrieve's value is the verdict, not the matrix.

We are equally honest about where the technique comes from. Isotrieve builds directly on prior work:

  • vec2vec (arXiv:2505.12540) — embedding-space translation via linear maps
  • mini-vec2vec — the lightweight, practical follow-up
  • Drift-Adapter — validates Procrustes and MLP-style approaches
  • Platonic Representation Hypothesis — the theoretical framing that makes shared embedding geometry plausible

Our contribution is engineering — library, CLI, quality gate, adapters, and benchmarks — not algorithmic novelty. If you are citing the underlying technique, cite that work; if you are citing the tool, cite this repo.

The gate lifecycle

Fit the mapping, gate it, migrate, and keep the old store until the verdict says it is safe. The gate is a checkpoint, not a feature bolted on at the end.

1. Calibrate

Embed the same ~2K in-domain texts with the source and target models, fit a ridge map, and save a .isotrieve artifact. See the Quick Start.

2. Gate

Run the quality gate on held-out samples. The verdict is driven by a model trained across benchmark runs, not a hand-tuned threshold. See the Quality Gate docs.

VerdictPredicted RetentionAction
PASS≥ 0.75Safe to migrate
WARN0.55 – 0.75Proceed with monitoring
FAIL< 0.55Do not ship; fix calibration or re-embed

3. Migrate

Transform the store in place with a migration that tracks progress in a manifest, so you can resume or roll back. The TypeScript migrateStore shows the lifecycle contract; the Python migrate_store is the production path.

import { migrateStore } from '@isotrieve/core';

const manifest = migrateStore(sourceStore, targetStore, mapping, {
  batchSize: 1024,               // vectors per batch
  manifestPath: './manifest.json',
  resume: true,                  // skip already-migrated batches
  onBatchComplete: (b) => {
    console.log(`Batch ${b.batchNum}: ${b.rowsProcessed} vectors`);
  },
});

console.log(manifest.rowsTotal, manifest.rowsMigrated);

4. Verify

Keep the old store until spot-checks and the gate pass on the new collection. A manifest makes rollback a replay, not a rebuild. See the migration docs for the full manifest contract.

When NOT to Use Isotrive

Isotrive is a cost/quality tradeoff, not a free lunch. Skip it (or re-embed instead) when:

  • Maximum quality matters more than cost — full re-embedding still wins on nDCG when the budget allows
  • K < 2000 — with fewer calibration pairs the gate usually lands in WARN; gather more in-domain pairs first
  • Calibration domain mismatches the corpus — out-of-domain pairs learn the wrong map, and no gate can fix a map learned on the wrong data
  • The gate returns FAIL — that is the answer, not an obstacle. Fix the calibration or re-embed.

Full details, including the When NOT to Use section of the main docs and the anti-patterns, cover the rest of the edge cases.