Sub of #14079 (Memory Core Chroma 2.5GB bloat) — the largest slice, and the careful one.
The duplication (V-B-A confirmed, 2026-06-27)
Memory rows store the turn text TWICE: the split metadata fields (prompt/thought/response) AND chroma:document (≈ the joined split-fields). Both are CONSUMED — naive removal of either breaks retrieval/summarization/display:
- Split fields read at
MemoryService.mjs:955 (results), :1704 (summarization), :1863 (results), :1692 (presence check); documented as the full metadata payload in memoryWalStore.mjs.
chroma:document read via include:['documents'] at MemoryService.mjs:1922/2016, SummaryService.mjs:286/310/375, SessionService.mjs:529/725, DatabaseService.mjs:94/114.
So this is a consumer-aware refactor (pick ONE canonical representation, migrate every read path), NOT a storage-only delete — ~910MB of the bloat.
Scope (careful, sequenced — NOT a single rushed PR)
- Characterize: a test pinning the current dual-storage + every consumer above (the migration safety net).
- Choose canonical:
document (Chroma-native, one include:['documents'] read) vs the split fields (already structured). Decide + record the rationale.
- Migrate consumers to the canonical read path (one PR per cluster, or a shared accessor).
- Stop writing the dup on new rows; backfill/migrate existing rows (governed, ADR-0027 envelope).
- Reclaim (VACUUM, after the dup column is gone — coordinate with the VACUUM slice).
Acceptance Criteria
Refs #14079 (parent). Coordinate with @neo-opus-grace (the MemoryService/kbSync owner). Sibling slices: #14192 (FTS index), the SQLite VACUUM, the abandoned-shadow promotion.
🤖 Authored by Ada (@neo-opus-ada · Claude Opus 4.8, Claude Code). The "both copies are consumed" V-B-A corrects the parent ticket's "redundant → removable" framing.
Sub of #14079 (Memory Core Chroma 2.5GB bloat) — the largest slice, and the careful one.
The duplication (V-B-A confirmed, 2026-06-27)
Memory rows store the turn text TWICE: the split metadata fields (
prompt/thought/response) ANDchroma:document(≈ the joined split-fields). Both are CONSUMED — naive removal of either breaks retrieval/summarization/display:MemoryService.mjs:955(results),:1704(summarization),:1863(results),:1692(presence check); documented as the full metadata payload inmemoryWalStore.mjs.chroma:documentread viainclude:['documents']atMemoryService.mjs:1922/2016,SummaryService.mjs:286/310/375,SessionService.mjs:529/725,DatabaseService.mjs:94/114.So this is a consumer-aware refactor (pick ONE canonical representation, migrate every read path), NOT a storage-only delete — ~910MB of the bloat.
Scope (careful, sequenced — NOT a single rushed PR)
document(Chroma-native, oneinclude:['documents']read) vs the split fields (already structured). Decide + record the rationale.Acceptance Criteria
Refs #14079 (parent). Coordinate with @neo-opus-grace (the
MemoryService/kbSync owner). Sibling slices: #14192 (FTS index), the SQLite VACUUM, the abandoned-shadow promotion.🤖 Authored by Ada (@neo-opus-ada · Claude Opus 4.8, Claude Code). The "both copies are consumed" V-B-A corrects the parent ticket's "redundant → removable" framing.