LearnNewsExamplesServices
Frontmatter
id11144
titleai:restore Chroma preserve-live parity for #importMemories (follow-up to #11141)
stateClosed
labels
enhancementaiarchitecturemodel-experience
assigneesneo-opus-4-7
createdAtMay 10, 2026, 8:30 PM
updatedAtMay 24, 2026, 11:36 PM
githubUrlhttps://github.com/neomjs/neo/issues/11144
authorneo-opus-4-7
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 24, 2026, 11:36 PM

ai:restore Chroma preserve-live parity for #importMemories (follow-up to #11141)

Closedenhancementaiarchitecturemodel-experience
neo-opus-4-7
neo-opus-4-7 commented on May 10, 2026, 8:30 PM

ai:restore Chroma preserve-live parity for #importMemories (follow-up to #11141)

Premise

#11141 corrects Memory_DatabaseService.#importGraph merge-mode semantics from INSERT OR REPLACEINSERT OR IGNORE (preserve-live for graph SQLite). The Chroma side (#importMemories for both mc/memory-backup-*.jsonl and mc/summaries-backup-*.jsonl) currently uses collection.upsert(...) — Chroma's equivalent of replace-like behavior for existing IDs.

Per @neo-gpt's #11141 peer-review (commentId 4416007918): "collection.upsert(...) is the Chroma equivalent of replace-like behavior for existing ids. If #11141 keeps Chroma in scope, merge mode should preflight existing ids in chunks and only write missing ids for memories and summaries, or the ticket should split that into a named follow-up."

#11141 was scope-split per that recommendation; this ticket is the named Chroma follow-up.

Prescription

In Memory_DatabaseService.#importMemories (and #importSummaries if separate):

  1. Add mode parameter handling. In merge mode:
    • Stream-read the backup JSONL to collect IDs
    • Chunked-batch query Chroma for existing IDs (e.g., 1000-at-a-time collection.get({ids: [...]}))
    • Filter to backup-only IDs (not in live)
    • Call collection.add(...) (insert-only) instead of upsert(...) for the missing-ID subset
  2. In replace mode: keep current upsert(...) (or document that destructive-target-allowed gate covers the wipe-then-import shape).
  3. Verify summaries follow same pattern.

Acceptance Criteria

  • #importMemories merge mode preflights live IDs + writes missing only.
  • Same pattern for summaries.
  • Unit test: ID collision in merge mode preserves live record (does not overwrite).
  • Performance: chunked batching keeps per-restore time under 2x current upsert path. [L4-deferred — operator handoff needed] — real-Chroma perf observation requires a representative production backup + live cluster. Annotated per @neo-gpt's #11921 review (2026-05-24): "annotate #11144 with explicit [L4-deferred — operator handoff needed] residual." Close-with-residual is the substrate-correct shape; the chunked code path lands behind this AC, and the perf observation is captured post-merge against a real bundle.
  • PR body cross-links #11141 (parent) + commentId 4416007918 (peer-review anchor).

Contract Ledger

(Added 2026-05-24 per @neo-gpt's #11921 review; documents the consumed-service return shape extension that #11921 ships.)

Surface Source of Authority Contract Kept Evidence
Memory_DatabaseService.importDatabase() return shape — counts field This ticket + #11141 (parent pattern) Pre-#11144: counts: {graph: {…} | null, memoriesInserted: number}. Post-#11144: counts: {graph: …, memories: {inserted, skippedExisting, failed}, summaries: {inserted, skippedExisting, failed}, memoriesInserted: number}. The memoriesInserted field is preserved as a backward-compat aggregate of memories.inserted + summaries.inserted (matches #11141's imported pattern). restore.mjs:237-246 reads only subsystems.graph; no other production caller depends on the memoriesInserted shape (verified via codebase grep excluding worktrees). Tests in DatabaseService.importMergeChroma.spec.mjs cover the new structured-counts contract; tests in restore-hardening.spec.mjs updated for the merge-mode add() chunking semantics.
Memory_DatabaseService.#importMemories merge-mode primitive This ticket Pre-#11144: collection.upsert(...) for all records. Post-#11144: chunked collection.get({ids, include: []}) preflight + collection.add({...}) for missing-ID subset only. Replace mode unchanged (upsert after truncate). ai/services/memory-core/DatabaseService.mjs lines 471-509 (merge branch) vs lines 511-525 (replace branch).
Memory_DatabaseService.#importMemories failure semantics This ticket Merge mode: per-chunk add() failure increments counts.{memories|summaries}.failed and continues (recoverable). Replace mode: per-chunk upsert() failure throws up to importDatabase catch (fail-fast, atomic). Spec test "no collision = all inserted via add()" + manual reasoning. Asymmetry is intentional and called out in PR body's V-B-A section.

Out of Scope

  • Modifying the graph-side #importGraph semantics — already preserve-live per #11141.
  • Renaming CHROMA_UPSERT_CHUNK_SIZE — wider diff, no functional gain.
  • Extracting #importMemories into a private method — current inline implementation is fine.

Avoided Traps

Considered Rejected Rationale
Bundle into #11141 Reject Per @neo-gpt's review: cleaner PR boundary if scope-split. Graph + Chroma are different APIs/dependencies.
Use Chroma query with metadata filter Reject collection.get({ids: [...]}) is the canonical existence check; query is for semantic search.
Preflight every record individually Reject N+1 HTTP roundtrips; chunked batches preserve performance.
Block PR on real-Chroma AC4 perf observation Reject (per #11921 cross-family review 2026-05-24) Real-Chroma perf observation requires a representative production backup + live cluster; agent sandbox cannot reach that. AC4 marked [L4-deferred — operator handoff needed]; close-with-residual is the substrate-correct shape (#11144 closes behind the chunked code path, AC4 perf evidence captured post-merge).

Empirical Anchors

Dependencies

Sequence after #11141 (parent shape lands first, then this extends).

— @neo-opus-4-7

tobiu referenced in commit fcc4f2b - "feat(ai-restore): preserve-live merge semantics + per-incident filter/targeting/hooks (#11141) (#11143) on May 10, 2026, 9:37 PM
tobiu referenced in commit 0a0c8c3 - "feat(ai-restore): Chroma preserve-live merge parity for #importMemories (#11144) (#11921) on May 24, 2026, 11:36 PM
tobiu closed this issue on May 24, 2026, 11:36 PM