Context
Follow-up to PR #10767 (feat(memory-core): surface active embedding provider in healthcheck (#10723)). Surfaced in @neo-gemini-3-1-pro's review (commentId IC_kwDODSospM8AAAABBTBnRg) as a non-blocking architectural observation.
PR #10767 surfaces aiConfig.chromaEmbeddingProvider state under providers.embedding. The Memory Core also has a SQLite-side embedding path controlled by aiConfig.neoEmbeddingProvider (env var NEO_EMBEDDING_PROVIDER), used for native-edge-graph operations on the SQLite engine. Currently the healthcheck does NOT surface the SQLite-side provider state.
The Problem
Operators can intentionally diverge NEO_EMBEDDING_PROVIDER from NEO_CHROMA_EMBEDDING_PROVIDER (different providers for different storage engines). When this happens:
- A misconfigured
NEO_EMBEDDING_PROVIDER (e.g. unset → silent default to gemini, while operator believes a local provider is wired) is undetectable from the healthcheck surface.
- Operators have no way to verify which SQLite-side provider was actually selected at boot.
- Even when both providers SHOULD match (most shared deployments), operators can't confirm the match without inspecting logs / re-running config through
node -e.
The PR #10767 docs already note this asymmetry in SharedDeployment.md ("A sibling override NEO_EMBEDDING_PROVIDER controls the SQLite-side embedding path; in most shared deployments it should match NEO_CHROMA_EMBEDDING_PROVIDER for consistency, but it can diverge when the SQLite and ChromaDB engines use different providers intentionally") — but doesn't surface it in healthcheck.
The Architectural Reality
ai/mcp/server/memory-core/services/HealthService.mjs#buildEmbeddingProviderBlock (added in PR #10767) — currently reads cfg.chromaEmbeddingProvider only
aiConfig.neoEmbeddingProvider — the SQLite-side provider config (parallel field to chromaEmbeddingProvider)
learn/agentos/SharedDeployment.md "Healthcheck Verification" — currently documents providers.embedding shape with single-provider field
The Fix
Two equivalent shapes worth considering:
Option A (parallel, structurally explicit): add a sibling providers.neoEmbedding block:
"providers": {
"embedding": { "active": "...", "host": "...", "model": "...", "dimensions": ... },
"neoEmbedding": { "active": "...", "host": "...", "model": "...", "dimensions": ... }
}
Option B (single block with chroma + neo subfields): restructure providers.embedding to surface both:
"providers": {
"embedding": {
"chroma": { "active": "...", "host": "...", "model": "...", "dimensions": ... },
"neo": { "active": "...", "host": "...", "model": "...", "dimensions": ... }
}
}
Option B is cleaner conceptually (both are embedding providers, just for different engines). Option A is closer to the current shape (less restructuring; backward-compatible if any consumer already reads providers.embedding).
Recommendation: Option B (clean restructure now while consumers are still few — only this session's #10770 follow-up references the shape; no public API consumers yet).
Acceptance Criteria
Contract Ledger (T3)
Per parent epic #10721 AC2 (Contract Completeness Gate). Matrix added 2026-05-06 in response to @neo-gpt's hand-back (suggested row encoded verbatim with editorial adjustments to pin the recommended option).
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
providers.embedding healthcheck payload (Option B restructure) |
#10773, #10721 AC2 + AC4, PR #10767 providers.embedding shipped shape, SharedDeployment.md ## Healthcheck Verification subsection |
Restructure providers.embedding to expose both Chroma-side (chroma.{active, host, model, dimensions}) and SQLite-side (neo.{active, host, model, dimensions}) provider selections. Surface an at-a-glance aligned/mismatch signal (e.g. aligned: bool derived from chroma.active === neo.active). |
Unrecognized provider values remain observable as non-secret error payloads inside their respective subfield (do not throw during healthcheck). Missing optional provider-specific host/model fields return null rather than throwing. If neoEmbeddingProvider env var is unset, neo.active defaults to 'gemini' matching the documented runtime fallback. |
Update learn/agentos/SharedDeployment.md ## Healthcheck Verification subsection — replace the flat providers.embedding JSON sample with the restructured chroma + neo subfields shape; add restructure-justification note in PR body covering "no public consumers exist yet, only this session's #10770 sibling work." Cross-check that #10770's providers.auth shape stays unaffected (sibling key, not nested). |
test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjs extended with: (1) same-provider case (chroma === neo === 'openAiCompatible', aligned: true), (2) diverged-provider case (chroma === 'gemini', neo === 'openAiCompatible', aligned: false), (3) neoEmbeddingProvider unset → defaults to 'gemini', (4) backward-compatibility check that the new shape reads correctly via existing wiring. Targeted run: npx playwright test test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjs --reporter=line. |
Out of Scope
- Provider-provider compatibility validation (e.g. dimensions match across both engines) — separate concern; this ticket is observability only
- Adding new provider types — this only surfaces existing config, not new functionality
- Healthcheck
providers.auth (separate follow-up #10770)
Related
- Adjacent shipped: PR #10767 (the current
providers.embedding block)
- Adjacent follow-up: #10770 (
providers.auth healthcheck observability — sibling shape for the auth dimension)
- Pattern precedents: #10176
buildIdentityBlock, #10127 buildTopologyBlock, #10723 buildEmbeddingProviderBlock (chroma side)
- Parent epic: #10721 (Shared deployment MVP completeness gaps) — this is post-trial-readiness polish, not blocking trial
Origin Session ID: 23b9cbcd-4938-4a46-b21a-0d48dd12e7e7
Retrieval Hint: query_raw_memories(query="healthcheck providers neoEmbedding SQLite-side symmetric chromaEmbeddingProvider 10767 10723 observability sibling")
Context
Follow-up to PR #10767 (
feat(memory-core): surface active embedding provider in healthcheck (#10723)). Surfaced in @neo-gemini-3-1-pro's review (commentIdIC_kwDODSospM8AAAABBTBnRg) as a non-blocking architectural observation.PR #10767 surfaces
aiConfig.chromaEmbeddingProviderstate underproviders.embedding. The Memory Core also has a SQLite-side embedding path controlled byaiConfig.neoEmbeddingProvider(env varNEO_EMBEDDING_PROVIDER), used for native-edge-graph operations on the SQLite engine. Currently the healthcheck does NOT surface the SQLite-side provider state.The Problem
Operators can intentionally diverge
NEO_EMBEDDING_PROVIDERfromNEO_CHROMA_EMBEDDING_PROVIDER(different providers for different storage engines). When this happens:NEO_EMBEDDING_PROVIDER(e.g. unset → silent default to gemini, while operator believes a local provider is wired) is undetectable from the healthcheck surface.node -e.The PR #10767 docs already note this asymmetry in
SharedDeployment.md("A sibling overrideNEO_EMBEDDING_PROVIDERcontrols the SQLite-side embedding path; in most shared deployments it should matchNEO_CHROMA_EMBEDDING_PROVIDERfor consistency, but it can diverge when the SQLite and ChromaDB engines use different providers intentionally") — but doesn't surface it in healthcheck.The Architectural Reality
ai/mcp/server/memory-core/services/HealthService.mjs#buildEmbeddingProviderBlock(added in PR #10767) — currently readscfg.chromaEmbeddingProvideronlyaiConfig.neoEmbeddingProvider— the SQLite-side provider config (parallel field tochromaEmbeddingProvider)learn/agentos/SharedDeployment.md"Healthcheck Verification" — currently documentsproviders.embeddingshape with single-provider fieldThe Fix
Two equivalent shapes worth considering:
Option A (parallel, structurally explicit): add a sibling
providers.neoEmbeddingblock:"providers": { "embedding": { "active": "...", "host": "...", "model": "...", "dimensions": ... }, "neoEmbedding": { "active": "...", "host": "...", "model": "...", "dimensions": ... } }Option B (single block with chroma + neo subfields): restructure
providers.embeddingto surface both:"providers": { "embedding": { "chroma": { "active": "...", "host": "...", "model": "...", "dimensions": ... }, "neo": { "active": "...", "host": "...", "model": "...", "dimensions": ... } } }Option B is cleaner conceptually (both are embedding providers, just for different engines). Option A is closer to the current shape (less restructuring; backward-compatible if any consumer already reads
providers.embedding).Recommendation: Option B (clean restructure now while consumers are still few — only this session's #10770 follow-up references the shape; no public API consumers yet).
Acceptance Criteria
buildEmbeddingProviderBlock(or successor function) reads BOTHchromaEmbeddingProviderANDneoEmbeddingProvider; healthcheck surfaces bothlearn/agentos/SharedDeployment.mdHealthcheck Verification subsection updated to show the new shapeContract Ledger (T3)
Per parent epic #10721 AC2 (Contract Completeness Gate). Matrix added 2026-05-06 in response to @neo-gpt's hand-back (suggested row encoded verbatim with editorial adjustments to pin the recommended option).
providers.embeddinghealthcheck payload (Option B restructure)providers.embeddingshipped shape,SharedDeployment.md## Healthcheck Verificationsubsectionproviders.embeddingto expose both Chroma-side (chroma.{active, host, model, dimensions}) and SQLite-side (neo.{active, host, model, dimensions}) provider selections. Surface an at-a-glance aligned/mismatch signal (e.g.aligned: boolderived fromchroma.active === neo.active).errorpayloads inside their respective subfield (do not throw during healthcheck). Missing optional provider-specifichost/modelfields returnnullrather than throwing. IfneoEmbeddingProviderenv var is unset,neo.activedefaults to'gemini'matching the documented runtime fallback.learn/agentos/SharedDeployment.md## Healthcheck Verificationsubsection — replace the flatproviders.embeddingJSON sample with the restructuredchroma+neosubfields shape; add restructure-justification note in PR body covering "no public consumers exist yet, only this session's #10770 sibling work." Cross-check that #10770'sproviders.authshape stays unaffected (sibling key, not nested).test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjsextended with: (1) same-provider case (chroma === neo === 'openAiCompatible',aligned: true), (2) diverged-provider case (chroma === 'gemini',neo === 'openAiCompatible',aligned: false), (3)neoEmbeddingProviderunset → defaults to'gemini', (4) backward-compatibility check that the new shape reads correctly via existing wiring. Targeted run:npx playwright test test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjs --reporter=line.Out of Scope
providers.auth(separate follow-up #10770)Related
providers.embeddingblock)providers.authhealthcheck observability — sibling shape for the auth dimension)buildIdentityBlock, #10127buildTopologyBlock, #10723buildEmbeddingProviderBlock(chroma side)Origin Session ID: 23b9cbcd-4938-4a46-b21a-0d48dd12e7e7
Retrieval Hint:
query_raw_memories(query="healthcheck providers neoEmbedding SQLite-side symmetric chromaEmbeddingProvider 10767 10723 observability sibling")