Context
The #12142/#12152 defrag-path work surfaced that the Chroma persistence topology has drifted off-piste, and a deep audit (Origin Session below) confirmed it. The live local store is one daemon serving a folder named knowledge-base that actually holds all realms — neo-knowledge-base (26,405 vecs), neo-agent-memory (14,722), neo-agent-sessions (1,075), neo-native-graph (612) — plus a stale chroma/memory-core/ folder (pre-ADR-0003 federated leftover, ~11k stale vecs, last written 2026-05-16) and ~1,134 leaked test-* collections.
Meanwhile the cloud deployment (ai/deploy/docker-compose.yml) already runs one chroma container with one flat volume (chroma-data:/chroma/chroma). So local (two folders, --path …/knowledge-base) and cloud (one flat volume) have diverged — there is no dev/prod parity. Operator decision: one flat store everywhere.
The Problem
- Misleading name. The persist dir named
knowledge-base holds every realm — it reads as "MC stores into the KB." #12152 enshrined engines.chroma.dataDir = …/knowledge-base as the SSOT, codifying the confusion.
- Tech debt. The stale
chroma/memory-core/ folder is a federated-era leftover; the live data is in the served store. It does not exist in cloud at all.
- Privacy landmine.
ai/scripts/maintenance/uploadKnowledgeBase.mjs runs zip -r neo-ai-data.zip .neo-ai-data — the whole private tree (199 MB memory-core-graph.sqlite, all memories, mailbox). downloadKnowledgeBase.mjs extracts that over .neo-ai-data — which would clobber a user's MC. Currently latent (the shipped v12.1.0 asset is the clean 2.2 MB chroma-neo-knowledge-base.zip; the script drifted to .neo-ai-data on 2026-04-06 and the asset name no longer matches → the path is effectively unreleased/broken). The unification turned the once-clean "zip the KB folder" into a leak.
- No parity. Local two-folder layout vs cloud one-flat-volume.
The Architectural Reality
- ChromaDB: one daemon ⟺ one persist
--path; HNSW indices are per-collection / per-segment — coexistence in one store does not degrade per-collection search latency/recall. The UUID dirs under the persist root are the independent hnswlib indices.
- Cloud (
docker-compose.yml): one chroma container (chromadb/chroma:1.5.9), chroma-data:/chroma/chroma; kb-server + mc-server + orchestrator connect via NEO_CHROMA_HOST=chroma / NEO_CHROMA_PORT=8000; NEO_ORCHESTRATOR_CHROMA_DAEMON_ENABLED=false (the container owns the daemon). Local: the orchestrator launches chroma run --path .neo-ai-data/chroma/knowledge-base.
- Isolation = collection names + metadata, not folders (
learn/agentos/cloud-deployment/Overview.md: "tenant isolation is enforced by the identity tuple + write-side stamping + read-side filter, not by physical separation"). MC connects host:port; dataDir is consumed only by the on-disk maintenance scripts.
- Recovery model: KB = rebuildable cache (
ai:sync-kb); MC = irreplaceable store (backup/restore).
The Decision (locked)
One flat Chroma store named unified, identical shape local and cloud — local .neo-ai-data/chroma/unified, cloud volume mounted at /chroma/unified (retiring the confusing /chroma/chroma). One daemon/container, no per-realm folders. KB / MC / tenant separation is enforced at the layers that exist everywhere — collection names + metadata (isolation), collection-scoped export (shipping), KB-as-cache / MC-as-store backup model (recovery) — never by directory or daemon split. Local conforms to the cloud reference; the two-folder layout and the stale memory-core folder are retired.
Decision Record impact
amends ADR 0003 (Chroma Topology — Unified Only): preserves its one-daemon core decision; adds the flat-store naming, the local↔cloud parity mandate, and the separation-layer model; retires the federated-leftover local two-folder layout. Touches ADR 0014 (Cloud Deployment Topology).
ADR successor-risk: adr-amendment-required — artifact this epic; ADR 0003 Accepted 2026-05-09; evidence (docker-compose.yml one-container + flat volume; uploadKnowledgeBase.mjs whole-dir zip; live store collection inventory; Overview.md topology anchor); route amendment (a new ADR that amends 0003), not silent bypass.
Contract Ledger (cross-cutting surfaces)
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
engines.chroma.dataDir (persist-path SSOT) |
ai/config.template.mjs |
resolves to …/chroma/unified (was …/knowledge-base) |
none — config is SSOT |
amending ADR + cloud-deployment/Configuration.md |
#12152 |
chroma daemon --path (local) + cloud volume mount |
orchestrator TaskDefinitions.mjs / docker-compose.yml |
unified everywhere; kill /chroma/chroma |
n/a |
amending ADR |
docker-compose.yml:22 |
| KB release artifact |
uploadKnowledgeBase.mjs / downloadKnowledgeBase.mjs |
collection-scoped export/import of neo-knowledge-base ONLY |
n/a |
guide |
uploadKnowledgeBase.mjs zip -r .neo-ai-data |
| defrag targets |
defragChromaDB.mjs TARGETS |
two collection-groups over the one store (paths converge) |
n/a |
defrag docs |
defragChromaDB.mjs:67-86 |
Sub-tickets (work breakdown)
- Amending ADR — author the decision record (one flat
unified store; parity; separation layers) amending ADR 0003 + touching ADR 0014.
- Rename + migrate (core) — SSOT value →
…/chroma/unified; orchestrator --path → unified; cloud compose volume → /chroma/unified + persist-dir; one-time migration (move data, delete stale memory-core). Builds on #12152.
defragChromaDB.mjs — two TARGETS → two collection-groups over the one unified store; keep the #12140 whole-store keep-set.
- Release-pipeline privacy fix ⚠️ (highest priority — latent leak) —
uploadKnowledgeBase.mjs collection-scoped export of neo-knowledge-base only (never zip -r .neo-ai-data); downloadKnowledgeBase.mjs collection-scoped import (never dir-restore); publish.mjs wiring; reconcile the neo-ai-data.zip vs chroma-neo-knowledge-base.zip name drift.
- Guide updates —
learn/agentos/cloud-deployment/* (Overview, Day0Tutorial, Configuration) + defrag/backup docs.
Companions (existing)
#12152 — the engines.chroma.dataDir SSOT foundation (keeper; sub-task 1 / merge precedes the rename).
#12143 — test-pollution leak → ephemeral test store (required so we never ship/defrag test-*).
#12139 — orchestrator owns the one daemon, servers as pure external clients (aligned; nests here).
Acceptance Criteria
Out of Scope
- Multi-tenant KB-collection HNSW scaling — the one
neo-knowledge-base collection holds all tenants (metadata-filtered); if that becomes a perf concern it's a separate axis, not this epic.
- Two-daemon / federated topology — explicitly rejected (cloud one-container forbids it).
Avoided Traps
- Two daemons / two live folders (an earlier proposal in the originating analysis) — structurally impossible under the cloud one-container model; would break dev/prod parity.
- Folder-level KB shipping — unsafe post-unification (would leak MC); collection-scoped export is the only correct mechanism and works identically in cloud (where no KB folder exists).
Related
#12152, #12143, #12139, #12142; ADR 0003, ADR 0014.
Origin Session ID: efd8dc2e-2052-4089-814a-ab22cd8c6a62
Retrieval Hint: "one flat unified chroma store local cloud parity rename knowledge-base memory-core"
Context
The
#12142/#12152defrag-path work surfaced that the Chroma persistence topology has drifted off-piste, and a deep audit (Origin Session below) confirmed it. The live local store is one daemon serving a folder namedknowledge-basethat actually holds all realms —neo-knowledge-base(26,405 vecs),neo-agent-memory(14,722),neo-agent-sessions(1,075),neo-native-graph(612) — plus a stalechroma/memory-core/folder (pre-ADR-0003 federated leftover, ~11k stale vecs, last written 2026-05-16) and ~1,134 leakedtest-*collections.Meanwhile the cloud deployment (
ai/deploy/docker-compose.yml) already runs onechromacontainer with one flat volume (chroma-data:/chroma/chroma). So local (two folders,--path …/knowledge-base) and cloud (one flat volume) have diverged — there is no dev/prod parity. Operator decision: one flat store everywhere.The Problem
knowledge-baseholds every realm — it reads as "MC stores into the KB."#12152enshrinedengines.chroma.dataDir = …/knowledge-baseas the SSOT, codifying the confusion.chroma/memory-core/folder is a federated-era leftover; the live data is in the served store. It does not exist in cloud at all.ai/scripts/maintenance/uploadKnowledgeBase.mjsrunszip -r neo-ai-data.zip .neo-ai-data— the whole private tree (199 MBmemory-core-graph.sqlite, all memories, mailbox).downloadKnowledgeBase.mjsextracts that over.neo-ai-data— which would clobber a user's MC. Currently latent (the shipped v12.1.0 asset is the clean 2.2 MBchroma-neo-knowledge-base.zip; the script drifted to.neo-ai-dataon 2026-04-06 and the asset name no longer matches → the path is effectively unreleased/broken). The unification turned the once-clean "zip the KB folder" into a leak.The Architectural Reality
--path; HNSW indices are per-collection / per-segment — coexistence in one store does not degrade per-collection search latency/recall. The UUID dirs under the persist root are the independent hnswlib indices.docker-compose.yml): onechromacontainer (chromadb/chroma:1.5.9),chroma-data:/chroma/chroma;kb-server+mc-server+orchestratorconnect viaNEO_CHROMA_HOST=chroma/NEO_CHROMA_PORT=8000;NEO_ORCHESTRATOR_CHROMA_DAEMON_ENABLED=false(the container owns the daemon). Local: the orchestrator launcheschroma run --path .neo-ai-data/chroma/knowledge-base.learn/agentos/cloud-deployment/Overview.md: "tenant isolation is enforced by the identity tuple + write-side stamping + read-side filter, not by physical separation"). MC connects host:port;dataDiris consumed only by the on-disk maintenance scripts.ai:sync-kb); MC = irreplaceable store (backup/restore).The Decision (locked)
One flat Chroma store named
unified, identical shape local and cloud — local.neo-ai-data/chroma/unified, cloud volume mounted at/chroma/unified(retiring the confusing/chroma/chroma). One daemon/container, no per-realm folders. KB / MC / tenant separation is enforced at the layers that exist everywhere — collection names + metadata (isolation), collection-scoped export (shipping), KB-as-cache / MC-as-store backup model (recovery) — never by directory or daemon split. Local conforms to the cloud reference; the two-folder layout and the stalememory-corefolder are retired.Decision Record impact
amends ADR 0003(Chroma Topology — Unified Only): preserves its one-daemon core decision; adds the flat-store naming, the local↔cloud parity mandate, and the separation-layer model; retires the federated-leftover local two-folder layout. TouchesADR 0014(Cloud Deployment Topology).ADR successor-risk: adr-amendment-required— artifact this epic; ADR 0003 Accepted 2026-05-09; evidence (docker-compose.ymlone-container + flat volume;uploadKnowledgeBase.mjswhole-dir zip; live store collection inventory;Overview.mdtopology anchor); route amendment (a new ADR that amends 0003), not silent bypass.Contract Ledger (cross-cutting surfaces)
engines.chroma.dataDir(persist-path SSOT)ai/config.template.mjs…/chroma/unified(was…/knowledge-base)cloud-deployment/Configuration.md#12152--path(local) + cloud volume mountTaskDefinitions.mjs/docker-compose.ymlunifiedeverywhere; kill/chroma/chromadocker-compose.yml:22uploadKnowledgeBase.mjs/downloadKnowledgeBase.mjsneo-knowledge-baseONLYuploadKnowledgeBase.mjszip -r .neo-ai-datadefragChromaDB.mjsTARGETSdefragChromaDB.mjs:67-86Sub-tickets (work breakdown)
unifiedstore; parity; separation layers) amending ADR 0003 + touching ADR 0014.…/chroma/unified; orchestrator--path→ unified; cloud compose volume →/chroma/unified+ persist-dir; one-time migration (move data, delete stalememory-core). Builds on#12152.defragChromaDB.mjs— twoTARGETS→ two collection-groups over the oneunifiedstore; keep the#12140whole-store keep-set.uploadKnowledgeBase.mjscollection-scoped export ofneo-knowledge-baseonly (neverzip -r .neo-ai-data);downloadKnowledgeBase.mjscollection-scoped import (never dir-restore);publish.mjswiring; reconcile theneo-ai-data.zipvschroma-neo-knowledge-base.zipname drift.learn/agentos/cloud-deployment/*(Overview, Day0Tutorial, Configuration) + defrag/backup docs.Companions (existing)
#12152— theengines.chroma.dataDirSSOT foundation (keeper; sub-task 1 / merge precedes the rename).#12143— test-pollution leak → ephemeral test store (required so we never ship/defragtest-*).#12139— orchestrator owns the one daemon, servers as pure external clients (aligned; nests here).Acceptance Criteria
unifiedlocal + cloud; noknowledge-base/memory-coresubfolders; stalememory-coredeleted.#12143test-pollution removed; tests use an ephemeral store.topology: 'unified'still accurate.Out of Scope
neo-knowledge-basecollection holds all tenants (metadata-filtered); if that becomes a perf concern it's a separate axis, not this epic.Avoided Traps
Related
#12152,#12143,#12139,#12142; ADR 0003, ADR 0014.Origin Session ID: efd8dc2e-2052-4089-814a-ab22cd8c6a62 Retrieval Hint: "one flat unified chroma store local cloud parity rename knowledge-base memory-core"