Filed 2026-05-08 as M6 sub-issue 4 (memory-core) — fourth and final per-server SDK migration in the M6 epic #10986 sequence (KB → GH-WF → NL → MC). Sequenced LAST because memory-core is the most complex server: 13 services + services/lifecycle/ subdir + sibling managers/ subdir + custom boot() override + multiple irreducible per-server helpers (stdio identity resolution, sibling-concurrency log, wake substrate, identity-spoof guard).
Per parent M6 epic #10986 §The Fix and learn/agentos/v13-path.md §3 D4: services move from ai/mcp/server/memory-core/services/*.mjs + ai/mcp/server/memory-core/managers/*.mjs → ai/services/memory-core/*.mjs + ai/services/memory-core/managers/*.mjs. ai/services.mjs becomes a re-export aggregator with stable Memory_* alias contracts. Linear sequencing rationale (per parent epic Avoided Traps): each migration touches ai/services.mjs import paths, so concurrent PRs would merge-conflict at the SDK aggregator. MC merges last.
The Problem
Memory-core services + managers currently live under two sibling directories:
12 of 17 service+manager files are exposed via ai/services.mjs's aliasing layer (Memory_* prefixed). 8 files are internal — not currently SDK-exported (CoalescingEngineService, FileSystemIngestor, MailboxService, PermissionService, WakeSubscriptionService, WebhookDeliveryService, toolService, AbstractVectorManager, CollectionProxy). Wait — that's 9 internal; combined with 12 SDK = 21 entities total.
The friction (per parent epic #10986 framing): same as siblings — on-disk shape conflates "service implementation" with "MCP transport endpoint". Memory-core specifically suffers more from this because (a) more services overall, (b) the managers/ sibling dir is structurally orthogonal to services/ but lives inside the same MCP-server tree, conflating "transport endpoint owner" with "data/ChromaDB layer abstraction".
This sub-issue normalizes memory-core to the existing ai/services/ConceptService.mjs flat-shape precedent, mirroring the pattern established by sibling sub-issues #10991 (KB), #10993 (GH-WF), and #10994 (NL).
The Architectural Reality
Current state (verified 2026-05-08 via ls ai/mcp/server/memory-core/services/ + ls ai/mcp/server/memory-core/managers/ + grep on ai/services.mjs):
MC Server.mjs reduced 580 → 417 LOC during M2 migration (less than other migrations because of irreducible per-server complexity)
Custom boot() override at ai/mcp/server/memory-core/Server.mjs:148 preserves stdio-identity-resolution-between-services-and-healthcheck order per #10249
Update ai/services.mjs import paths (lines 43-54) — alias names (Memory_*) preserved; only import paths change.
Update ai/mcp/server/memory-core/Server.mjs custom boot() override at line 148 — import paths for all directly-imported services (GraphService, HealthService, SessionService, InferenceLifecycleService at services/lifecycle/, WakeSubscriptionService, CoalescingEngineService, {listTools, callTool} from toolService.mjs).
Update MC config.mjs / openapi.yaml / logger.mjs if they reference service paths.
Remove the now-empty ai/mcp/server/memory-core/services/ + ai/mcp/server/memory-core/managers/ directories.
Verify operator scripts (ai:summarize-sessions, ai:run-sandman, ai:backup, ai:defrag-*) work unchanged via the preserved Memory_* aliases.
Class names UNCHANGED. Service file names UNCHANGED. Only physical locations change.
Custom-boot-order preservation: MC's boot() override exists to preserve stdio-identity-resolution-between-services-and-healthcheck order per #10249, plus identity-spoof guard via beforeToolDispatch per #10145, plus wake-substrate per #10437 / #10438, plus per-#10241 retry pattern in bindAgentIdentity. Migration must NOT change boot order — only import paths inside the override.
Decision: managers/ moves under ai/services/memory-core/managers/ (preserving sibling subdir structure within the new flat root). Rationale: keep the architectural distinction between "transport-bound services" and "data-layer abstractions" visible. Alternative (flatten managers/ into services/) considered + rejected (see Avoided Traps).
Old ai/mcp/server/memory-core/services/ + ai/mcp/server/memory-core/managers/ directories removed.
AC2:ai/services.mjs continues exporting all 12 currently-aliased MC services + managers (Memory_Service, Memory_DatabaseService, Memory_SessionService, Memory_HealthService, Memory_GraphService, Memory_SummaryService, Memory_ChromaLifecycleService, Memory_InferenceLifecycleService, Memory_LifecycleService, Memory_TextEmbeddingService, Memory_ChromaManager, Memory_StorageRouter); only import paths change.
AC3:ai/mcp/server/memory-core/Server.mjsboot() override updated to import all directly-referenced services (GraphService, HealthService, SessionService, InferenceLifecycleService, WakeSubscriptionService, CoalescingEngineService, toolService) from new SDK paths. Custom boot order PRESERVED including stdio-identity-resolution-between-services-and-healthcheck per #10249.
AC4: Operator scripts + daemons + tests consume MC services unchanged (no code changes required at consumer sites; aliases hold).
AC5: Internal cross-references between MC services + managers + lifecycle/ subdir updated. AbstractVectorManager base-class extension chain works post-migration; CollectionProxy still resolves; lifecycle services still cross-reference correctly.
AC6: All 258 MC unit specs pass post-migration (per #10977 PR5 evidence baseline; the 5 pre-existing Bucket G flakes remain pre-existing — track separately, do not regress new failures).
AC7: Integration row green post-migration on the PR.
AC11: Custom Server.mjs hooks preserved (wrapDispatch, beforeToolDispatch for AuthMiddleware identity-spoof guard, buildRequestContext for SSE OIDC, onSessionClosed for queueSummarizationJob + CoalescingEngine cleanup, createMcpServer override for CoalescingEngineService bind).
AC12: Wake substrate preservation — WakeSubscriptionService.init() pre-mcpServer per #10437 still fires; identity binding fire-and-forget IIFE per #10438 single-error-boundary preserved.
AC13: PR description includes a brief diff summary table (file count moved, alias-preserved count, consumer-side breakage delta — should be zero).
Renaming MC services or class names — physical move only.
Reshaping MC's custom boot() order — preserved as-is per PR #10977 rationale; this sub-issue does NOT revisit stdio-identity-resolution architectural decision.
Touching ai/mcp/server/shared/services/* — shared cross-cutting services (AuthMiddleware, RequestContextService, StdioIdentityResolver) stay at the shared boundary.
Promoting internal services to SDK-exported — CoalescingEngineService, FileSystemIngestor, MailboxService, PermissionService, WakeSubscriptionService, WebhookDeliveryService, AbstractVectorManager, CollectionProxy stay internal. External exposure should be a deliberate decision.
Reshaping managers/ vs services/lifecycle/ subdir architecture — physical move preserves both subdirs as-is; future ticket if reshape needed.
<Server>_<ServiceName> namespace prefix collapse — preserved through M6 per parent epic Out-of-Scope.
Bucket G unit-spec flakes (#10941/#10937 etc.) — pre-existing, tracked separately under #10924; do NOT attempt to fix during migration.
ai/services.mjs re-organization — this sub-issue updates MC import paths only.
Wake substrate reshape (#10437/#10438) — preserved as-is.
Avoided Traps / Gold Standards Rejected
Rejected: only move SDK-exported services, leave internal services + subdirs in old location. Splits MC across two top-level dirs categorized by export-status; creates ongoing cognitive cost for MC authors.
Rejected: flatten managers/ into ai/services/memory-core/ (no subdir). The sibling subdir distinction reflects an architectural layering — managers are data-layer abstractions (ChromaDB / storage routing), services are transport/business-logic. Preserving the subdir keeps that distinction visible. Flattening would force readers to back-derive the layer from class names.
Rejected: flatten services/lifecycle/ into ai/services/memory-core/. Same reasoning — lifecycle subdir reflects bootstrap/teardown-phase services that are conceptually distinct from request-handling services.
Rejected: rename Memory_* aliases to drop prefix during migration. Breaks every operator script + daemon + test consumer.
Rejected: refactor makeSafe Zod wrapping pattern alongside the move. Increases PR risk catastrophically given MC's 258-spec unit surface.
Rejected: convert MC's custom boot() to canonical getDependentServices() flow. Architectural reshape; rationale for custom order is preserved per PR #10977 — stdio-identity-resolution-between-services-and-healthcheck is load-bearing for the boot-snapshot identity state per #10249.
Rejected: parallel cadence with sibling M6 sub-issues (KB / GH-WF / NL). Per parent epic #10986 Avoided Traps: linear sequencing keeps ai/services.mjs under single-edit lock. MC merges last.
Rejected: gradual migration via indirection layer. Clean cut-over is simpler — even at 21-entity scope, the import-path update is mechanical.
Rejected: split MC migration into "services" + "managers" + "boot-imports" PRs. All three touch the same ai/services.mjs block; combining is cleaner. Splitting would violate the linear-sequencing constraint AND inflate PR overhead 3x.
Rejected: attempt to fix Bucket G flakes during migration. Out-of-scope per ticket #10924; conflating migration with flake-fixing risks rolling back productive substrate work if flake fixes regress.
M2 per-server migration precedent:#10977 — memory-core migration to BaseServer + introduced beforeToolDispatch hook. The same Server.mjs that received the M2 BaseServer extension hooks now receives M6's import-path updates inside the boot() override.
Context
Filed 2026-05-08 as M6 sub-issue 4 (memory-core) — fourth and final per-server SDK migration in the M6 epic #10986 sequence (KB → GH-WF → NL → MC). Sequenced LAST because memory-core is the most complex server: 13 services +
services/lifecycle/subdir + siblingmanagers/subdir + customboot()override + multiple irreducible per-server helpers (stdio identity resolution, sibling-concurrency log, wake substrate, identity-spoof guard).Per parent M6 epic #10986 §The Fix and
learn/agentos/v13-path.md§3 D4: services move fromai/mcp/server/memory-core/services/*.mjs+ai/mcp/server/memory-core/managers/*.mjs→ai/services/memory-core/*.mjs+ai/services/memory-core/managers/*.mjs.ai/services.mjsbecomes a re-export aggregator with stableMemory_*alias contracts. Linear sequencing rationale (per parent epic Avoided Traps): each migration touchesai/services.mjsimport paths, so concurrent PRs would merge-conflict at the SDK aggregator. MC merges last.The Problem
Memory-core services + managers currently live under two sibling directories:
ai/mcp/server/memory-core/services/— 13 service files +lifecycle/subdir (3 services) +toolService.mjsai/mcp/server/memory-core/managers/— 4 manager files (sibling-not-nested-under-services)12 of 17 service+manager files are exposed via
ai/services.mjs's aliasing layer (Memory_*prefixed). 8 files are internal — not currently SDK-exported (CoalescingEngineService,FileSystemIngestor,MailboxService,PermissionService,WakeSubscriptionService,WebhookDeliveryService,toolService,AbstractVectorManager,CollectionProxy). Wait — that's 9 internal; combined with 12 SDK = 21 entities total.The friction (per parent epic #10986 framing): same as siblings — on-disk shape conflates "service implementation" with "MCP transport endpoint". Memory-core specifically suffers more from this because (a) more services overall, (b) the
managers/sibling dir is structurally orthogonal toservices/but lives inside the same MCP-server tree, conflating "transport endpoint owner" with "data/ChromaDB layer abstraction".This sub-issue normalizes memory-core to the existing
ai/services/ConceptService.mjsflat-shape precedent, mirroring the pattern established by sibling sub-issues #10991 (KB), #10993 (GH-WF), and #10994 (NL).The Architectural Reality
Current state (verified 2026-05-08 via
ls ai/mcp/server/memory-core/services/+ls ai/mcp/server/memory-core/managers/+greponai/services.mjs):services/directory (13 services + 1 router + lifecycle/ subdir)MemoryService.mjsMemory_ServiceDatabaseService.mjsMemory_DatabaseServiceSessionService.mjsMemory_SessionServiceHealthService.mjsMemory_HealthServiceGraphService.mjsMemory_GraphServiceSummaryService.mjsMemory_SummaryServiceTextEmbeddingService.mjsMemory_TextEmbeddingServiceCoalescingEngineService.mjsFileSystemIngestor.mjsMailboxService.mjsPermissionService.mjsWakeSubscriptionService.mjsWebhookDeliveryService.mjstoolService.mjsservices/lifecycle/subdir (3 lifecycle services)ChromaLifecycleService.mjsMemory_ChromaLifecycleServiceInferenceLifecycleService.mjsMemory_InferenceLifecycleServiceSystemLifecycleService.mjsMemory_LifecycleServicemanagers/SIBLING subdir (4 ChromaDB/storage abstractions)ChromaManager.mjsMemory_ChromaManagerStorageRouter.mjsMemory_StorageRouterAbstractVectorManager.mjsCollectionProxy.mjsTotal: 21 entities (14 services + 3 lifecycle + 4 managers). 12 SDK-exported, 9 internal.
M2 BaseServer substrate (#10965 / PR #10977):
boot()override atai/mcp/server/memory-core/Server.mjs:148preserves stdio-identity-resolution-between-services-and-healthcheck order per #10249getServerMetadata,getToolService,getHealthService,getHealthExemptTools,wrapDispatch(RequestContextService binding for stdio identity),beforeToolDispatch(AuthMiddleware identity-spoof guard per #10145),buildRequestContext(SSE per-request OIDC binding),onSessionClosed(SSE: queueSummarizationJob + CoalescingEngine cleanup),logStartupStatus(Memory Core flavored output),createMcpServer(override chains super for CoalescingEngineService bind){listTools, callTool}fromtoolService.mjsresolveStdioIdentity,bindAgentIdentity(with #10241 retry pattern),logIdentityStatus,logCollectionStats,logSiblingConcurrencymakeSafe()Zod-wrapping (ai/services.mjs:219-228): wraps 10 of 12 SDK-exported services. NOT wrapped:Memory_ChromaManager,Memory_StorageRouter(managers/ — pure data-layer abstractions, no Zod schema needed).Existing flat-shape precedent + sibling sub-issues:
ai/services/ConceptService.mjs(Concept Ontology)managers/dir, deeper custom Server.mjs boot orderThe Fix
Per-file moves (21 entities across 3 dirs):
services/ flat moves (14 files: 13 services + toolService)
ai/mcp/server/memory-core/services/MemoryService.mjsai/services/memory-core/MemoryService.mjsai/mcp/server/memory-core/services/DatabaseService.mjsai/services/memory-core/DatabaseService.mjsai/mcp/server/memory-core/services/SessionService.mjsai/services/memory-core/SessionService.mjsai/mcp/server/memory-core/services/HealthService.mjsai/services/memory-core/HealthService.mjsai/mcp/server/memory-core/services/GraphService.mjsai/services/memory-core/GraphService.mjsai/mcp/server/memory-core/services/SummaryService.mjsai/services/memory-core/SummaryService.mjsai/mcp/server/memory-core/services/TextEmbeddingService.mjsai/services/memory-core/TextEmbeddingService.mjsai/mcp/server/memory-core/services/CoalescingEngineService.mjsai/services/memory-core/CoalescingEngineService.mjsai/mcp/server/memory-core/services/FileSystemIngestor.mjsai/services/memory-core/FileSystemIngestor.mjsai/mcp/server/memory-core/services/MailboxService.mjsai/services/memory-core/MailboxService.mjsai/mcp/server/memory-core/services/PermissionService.mjsai/services/memory-core/PermissionService.mjsai/mcp/server/memory-core/services/WakeSubscriptionService.mjsai/services/memory-core/WakeSubscriptionService.mjsai/mcp/server/memory-core/services/WebhookDeliveryService.mjsai/services/memory-core/WebhookDeliveryService.mjsai/mcp/server/memory-core/services/toolService.mjsai/services/memory-core/toolService.mjsservices/lifecycle/ subdir moves (3 files; whole subdir as a unit)
ai/mcp/server/memory-core/services/lifecycle/ChromaLifecycleService.mjsai/services/memory-core/lifecycle/ChromaLifecycleService.mjsai/mcp/server/memory-core/services/lifecycle/InferenceLifecycleService.mjsai/services/memory-core/lifecycle/InferenceLifecycleService.mjsai/mcp/server/memory-core/services/lifecycle/SystemLifecycleService.mjsai/services/memory-core/lifecycle/SystemLifecycleService.mjsmanagers/ sibling subdir moves (4 files; whole subdir as a unit, parallel to services/ flat)
ai/mcp/server/memory-core/managers/ChromaManager.mjsai/services/memory-core/managers/ChromaManager.mjsai/mcp/server/memory-core/managers/StorageRouter.mjsai/services/memory-core/managers/StorageRouter.mjsai/mcp/server/memory-core/managers/AbstractVectorManager.mjsai/services/memory-core/managers/AbstractVectorManager.mjsai/mcp/server/memory-core/managers/CollectionProxy.mjsai/services/memory-core/managers/CollectionProxy.mjsThen:
ai/services.mjsimport paths (lines 43-54) — alias names (Memory_*) preserved; only import paths change.ai/mcp/server/memory-core/Server.mjscustomboot()override at line 148 — import paths for all directly-imported services (GraphService, HealthService, SessionService, InferenceLifecycleService atservices/lifecycle/, WakeSubscriptionService, CoalescingEngineService,{listTools, callTool}from toolService.mjs).config.mjs/openapi.yaml/logger.mjsif they reference service paths.resolveStdioIdentity,bindAgentIdentity,logIdentityStatus,logCollectionStats,logSiblingConcurrency) survive migration without import-path drift.ai/mcp/server/memory-core/services/+ai/mcp/server/memory-core/managers/directories.ai:summarize-sessions,ai:run-sandman,ai:backup,ai:defrag-*) work unchanged via the preservedMemory_*aliases.Class names UNCHANGED. Service file names UNCHANGED. Only physical locations change.
Custom-boot-order preservation: MC's
boot()override exists to preserve stdio-identity-resolution-between-services-and-healthcheck order per #10249, plus identity-spoof guard viabeforeToolDispatchper #10145, plus wake-substrate per #10437 / #10438, plus per-#10241 retry pattern inbindAgentIdentity. Migration must NOT change boot order — only import paths inside the override.Decision: managers/ moves under ai/services/memory-core/managers/ (preserving sibling subdir structure within the new flat root). Rationale: keep the architectural distinction between "transport-bound services" and "data-layer abstractions" visible. Alternative (flatten managers/ into services/) considered + rejected (see Avoided Traps).
Contract Ledger Matrix
ai/services/memory-core/<ServiceName>.mjspathslearn/agentos/post-M6-epicls ai/services/memory-core/confirms move; oldai/mcp/server/memory-core/{services,managers}/removedai/services.mjsMemory_*re-export aliasesai/services.mjsMC Server.boot()overrideServer.mjsJSDocmanagers/sibling-subdir structureai/services/memory-core/managers/subdir under the new flat rootresolveStdioIdentity,bindAgentIdentity, etc.)Acceptance Criteria
ai/services/memory-core/— 14 files (13 services + toolService.mjs)ai/services/memory-core/lifecycle/— 3 lifecycle servicesai/services/memory-core/managers/— 4 managersai/mcp/server/memory-core/services/+ai/mcp/server/memory-core/managers/directories removed.ai/services.mjscontinues exporting all 12 currently-aliased MC services + managers (Memory_Service,Memory_DatabaseService,Memory_SessionService,Memory_HealthService,Memory_GraphService,Memory_SummaryService,Memory_ChromaLifecycleService,Memory_InferenceLifecycleService,Memory_LifecycleService,Memory_TextEmbeddingService,Memory_ChromaManager,Memory_StorageRouter); only import paths change.ai/mcp/server/memory-core/Server.mjsboot()override updated to import all directly-referenced services (GraphService, HealthService, SessionService, InferenceLifecycleService, WakeSubscriptionService, CoalescingEngineService, toolService) from new SDK paths. Custom boot order PRESERVED including stdio-identity-resolution-between-services-and-healthcheck per #10249.Neo.setupClass()with a className matching old path, className updated to the new path.resolveStdioIdentity,bindAgentIdentitywith #10241 retry,logIdentityStatus,logCollectionStats,logSiblingConcurrency). Stdio identity binding + sibling-concurrency log work unchanged post-migration.wrapDispatch,beforeToolDispatchfor AuthMiddleware identity-spoof guard,buildRequestContextfor SSE OIDC,onSessionClosedfor queueSummarizationJob + CoalescingEngine cleanup,createMcpServeroverride for CoalescingEngineService bind).WakeSubscriptionService.init()pre-mcpServer per #10437 still fires; identity binding fire-and-forget IIFE per #10438 single-error-boundary preserved.Out of Scope
makeSafe()Zod schema reshape — wrapping pattern preserved as-is.boot()order — preserved as-is per PR #10977 rationale; this sub-issue does NOT revisit stdio-identity-resolution architectural decision.ai/mcp/server/shared/services/*— shared cross-cutting services (AuthMiddleware, RequestContextService, StdioIdentityResolver) stay at the shared boundary.managers/vsservices/lifecycle/subdir architecture — physical move preserves both subdirs as-is; future ticket if reshape needed.<Server>_<ServiceName>namespace prefix collapse — preserved through M6 per parent epic Out-of-Scope.ai/services.mjsre-organization — this sub-issue updates MC import paths only.Avoided Traps / Gold Standards Rejected
managers/intoai/services/memory-core/(no subdir). The sibling subdir distinction reflects an architectural layering — managers are data-layer abstractions (ChromaDB / storage routing), services are transport/business-logic. Preserving the subdir keeps that distinction visible. Flattening would force readers to back-derive the layer from class names.services/lifecycle/intoai/services/memory-core/. Same reasoning — lifecycle subdir reflects bootstrap/teardown-phase services that are conceptually distinct from request-handling services.Memory_*aliases to drop prefix during migration. Breaks every operator script + daemon + test consumer.makeSafeZod wrapping pattern alongside the move. Increases PR risk catastrophically given MC's 258-spec unit surface.boot()to canonicalgetDependentServices()flow. Architectural reshape; rationale for custom order is preserved per PR #10977 — stdio-identity-resolution-between-services-and-healthcheck is load-bearing for the boot-snapshot identity state per #10249.ai/services.mjsunder single-edit lock. MC merges last.ai/services.mjsblock; combining is cleaner. Splitting would violate the linear-sequencing constraint AND inflate PR overhead 3x.Related
learn/agentos/v13-path.md§3 D4 + §4 M6beforeToolDispatchhook. The sameServer.mjsthat received the M2 BaseServer extension hooks now receives M6's import-path updates inside theboot()override.beforeToolDispatchWakeSubscriptionService.init()pre-mcpServerbindAgentIdentityretry patternOrigin Session ID:
33ed57b5-10ed-491d-8b8f-ce5f1223ec38Retrieval Hint:
query_raw_memories(query="M6 SDK migration memory-core services managers lifecycle flat shape ai/services/memory-core Memory_ aliases boot override stdio identity")