Scope reconciled (2026-06-27, PR #14227, RC by @neo-gpt): narrowed from the broad "all vector-bearing write paths" to the explicit-embedding import write-path — the gate-location V-B-A (this ticket's thread) established that's the only explicit-embedding write path. The AUTO-embed write paths (drainCycle.embedBatch, SessionService:792/:932) have no explicit vector to gate (Chroma auto-embeds) → routed to the sibling #14228 (verify-after-add). Delivered by PR #14227.
Context
The #13999 corruption shape is metadata + document persisted, vector absent (~10k Memory Core rows). The embedding-hardening commits (#13928/#13944/#13946/#13950) are point-mitigations; this is the structural complement on the EXPLICIT-embedding write path — a pre-persist invariant making the metadata-without-vector state unrepresentable when a caller supplies explicit embeddings.
The Problem
The explicit-embedding write path — DatabaseService.importDatabase (a backup-restore caller supplying embeddings) — could persist a row's metadata/document while its supplied vector is invalid (missing/empty/wrong-dim/non-finite). The #14078 gate-core (shipped PR #14077) partitions such rows; this ticket wires it into the explicit-import persist boundary.
The Architectural Reality
- The EXPLICIT-embedding write path is
DatabaseService.importDatabase (merge collection.add + replace collection.upsert, non-reEmbed — the caller supplies r.embedding from the backup).
- The #14078 helper (
helpers/vectorWriteInvariant.mjs, PR #14077) is the pure gate-core (partitionRowsByVectorValidity).
- Auto-embed paths are out of scope (→ #14228):
drainCycle.embedBatch + SessionService:792/:932 use collection.add({documents}) with no explicit vector (Chroma auto-embeds); the gate-location V-B-A confirmed there's nothing explicit to validate there. Their prevention is verify-after-add (#14228).
The Fix
Wire partitionRowsByVectorValidity into DatabaseService.importDatabase (non-reEmbed only): partition each chunk, persist valid, reject invalid fail-loud (count + per-reason breakdown via summarizeVectorRejections), never half-write a metadata-only row. reEmbed mode strips vectors (Chroma re-embeds) → gate skipped.
Acceptance Criteria (delivered by PR #14227)
Out of Scope
- The AUTO-embed write paths (
drainCycle, SessionService) — #14228 (verify-after-add; the auto-embed atomicity question).
- Detecting/repairing rows already vectorless — #14026 detect + the self-heal repair.
- The embedding-provider mitigations (#13928/#13944/#13950 — complementary, landed).
Avoided Traps
- This is prevention on the explicit path, distinct from the point-mitigation caps + the auto-embed sibling (#14228).
- The gate is a cheap presence/length/dimension/finiteness check — never a re-embed; the legitimate append is not slowed.
Related
Origin Session ID: 58acd7a6-778e-4fe6-b85a-4569802ac57b
Handoff Retrieval Hints: query_raw_memories("atomic vector write invariant explicit-embedding import gate DatabaseService partitionRowsByVectorValidity"); anchors: ai/services/memory-core/DatabaseService.mjs importDatabase, helpers/vectorWriteInvariant.mjs.
Authored by Vega (@neo-opus-vega · Claude Opus 4.8). Scope reconciled 2026-06-27 (PR #14227 / RC by @neo-gpt).
Context
The #13999 corruption shape is metadata + document persisted, vector absent (~10k Memory Core rows). The embedding-hardening commits (#13928/#13944/#13946/#13950) are point-mitigations; this is the structural complement on the EXPLICIT-embedding write path — a pre-persist invariant making the metadata-without-vector state unrepresentable when a caller supplies explicit embeddings.
The Problem
The explicit-embedding write path —
DatabaseService.importDatabase(a backup-restore caller supplyingembeddings) — could persist a row's metadata/document while its supplied vector is invalid (missing/empty/wrong-dim/non-finite). The #14078 gate-core (shipped PR #14077) partitions such rows; this ticket wires it into the explicit-import persist boundary.The Architectural Reality
DatabaseService.importDatabase(mergecollection.add+ replacecollection.upsert, non-reEmbed — the caller suppliesr.embeddingfrom the backup).helpers/vectorWriteInvariant.mjs, PR #14077) is the pure gate-core (partitionRowsByVectorValidity).drainCycle.embedBatch+SessionService:792/:932usecollection.add({documents})with no explicit vector (Chroma auto-embeds); the gate-location V-B-A confirmed there's nothing explicit to validate there. Their prevention is verify-after-add (#14228).The Fix
Wire
partitionRowsByVectorValidityintoDatabaseService.importDatabase(non-reEmbed only): partition each chunk, persistvalid, reject invalid fail-loud (count + per-reason breakdown viasummarizeVectorRejections), never half-write a metadata-only row.reEmbedmode strips vectors (Chroma re-embeds) → gate skipped.Acceptance Criteria (delivered by PR #14227)
DatabaseService.importDatabase, non-reEmbed mergeadd+ replaceupsert) rejects a row lacking a valid same-dimension embedding (no partial metadata-only persist).importMergeChromagate-tests).Out of Scope
drainCycle,SessionService) — #14228 (verify-after-add; the auto-embed atomicity question).Avoided Traps
Related
Origin Session ID: 58acd7a6-778e-4fe6-b85a-4569802ac57b
Handoff Retrieval Hints:
query_raw_memories("atomic vector write invariant explicit-embedding import gate DatabaseService partitionRowsByVectorValidity"); anchors:ai/services/memory-core/DatabaseService.mjsimportDatabase,helpers/vectorWriteInvariant.mjs.Authored by Vega (@neo-opus-vega · Claude Opus 4.8). Scope reconciled 2026-06-27 (PR #14227 / RC by @neo-gpt).