LearnNewsExamplesServices
Frontmatter
id10996
titleMigrate memory-core services + managers to flat SDK boundary
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-4-7
createdAtMay 8, 2026, 11:17 PM
updatedAtMay 15, 2026, 2:47 PM
githubUrlhttps://github.com/neomjs/neo/issues/10996
authorneo-opus-4-7
commentsCount1
parentIssue10986
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 9, 2026, 12:33 PM

Migrate memory-core services + managers to flat SDK boundary

Closedenhancementairefactoringarchitecture
neo-opus-4-7
neo-opus-4-7 commented on May 8, 2026, 11:17 PM

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 + 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/*.mjsai/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:

  • ai/mcp/server/memory-core/services/ — 13 service files + lifecycle/ subdir (3 services) + toolService.mjs
  • ai/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 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):

services/ directory (13 services + 1 router + lifecycle/ subdir)

File SDK alias SDK-exported
MemoryService.mjs Memory_Service
DatabaseService.mjs Memory_DatabaseService
SessionService.mjs Memory_SessionService
HealthService.mjs Memory_HealthService
GraphService.mjs Memory_GraphService
SummaryService.mjs Memory_SummaryService
TextEmbeddingService.mjs Memory_TextEmbeddingService
CoalescingEngineService.mjs (internal)
FileSystemIngestor.mjs (internal)
MailboxService.mjs (internal)
PermissionService.mjs (internal)
WakeSubscriptionService.mjs (internal — wired in MC Server.mjs boot)
WebhookDeliveryService.mjs (internal)
toolService.mjs (internal — MCP tool router)

services/lifecycle/ subdir (3 lifecycle services)

File SDK alias SDK-exported
ChromaLifecycleService.mjs Memory_ChromaLifecycleService
InferenceLifecycleService.mjs Memory_InferenceLifecycleService
SystemLifecycleService.mjs Memory_LifecycleService

managers/ SIBLING subdir (4 ChromaDB/storage abstractions)

File SDK alias SDK-exported
ChromaManager.mjs Memory_ChromaManager
StorageRouter.mjs Memory_StorageRouter
AbstractVectorManager.mjs (internal — base class for vector managers)
CollectionProxy.mjs (internal — Chroma collection abstraction)

Total: 21 entities (14 services + 3 lifecycle + 4 managers). 12 SDK-exported, 9 internal.

M2 BaseServer substrate (#10965 / PR #10977):

  • 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
  • Hooks used: getServerMetadata, 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)
  • Server.mjs imports services directly: GraphService, HealthService, SessionService, InferenceLifecycleService (lifecycle/), WakeSubscriptionService, CoalescingEngineService, plus {listTools, callTool} from toolService.mjs
  • Memory-core-private helpers preserved: resolveStdioIdentity, bindAgentIdentity (with #10241 retry pattern), logIdentityStatus, logCollectionStats, logSiblingConcurrency

makeSafe() 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)
  • #10991 (KB; 10 services), #10993 (GH-WF; 10 services + 2 helper subdirs), #10994 (NL; 9 services + custom boot)
  • MC extends the pattern with: more entities, sibling managers/ dir, deeper custom Server.mjs boot order

The Fix

Per-file moves (21 entities across 3 dirs):

services/ flat moves (14 files: 13 services + toolService)

Source path Destination path
ai/mcp/server/memory-core/services/MemoryService.mjs ai/services/memory-core/MemoryService.mjs
ai/mcp/server/memory-core/services/DatabaseService.mjs ai/services/memory-core/DatabaseService.mjs
ai/mcp/server/memory-core/services/SessionService.mjs ai/services/memory-core/SessionService.mjs
ai/mcp/server/memory-core/services/HealthService.mjs ai/services/memory-core/HealthService.mjs
ai/mcp/server/memory-core/services/GraphService.mjs ai/services/memory-core/GraphService.mjs
ai/mcp/server/memory-core/services/SummaryService.mjs ai/services/memory-core/SummaryService.mjs
ai/mcp/server/memory-core/services/TextEmbeddingService.mjs ai/services/memory-core/TextEmbeddingService.mjs
ai/mcp/server/memory-core/services/CoalescingEngineService.mjs ai/services/memory-core/CoalescingEngineService.mjs
ai/mcp/server/memory-core/services/FileSystemIngestor.mjs ai/services/memory-core/FileSystemIngestor.mjs
ai/mcp/server/memory-core/services/MailboxService.mjs ai/services/memory-core/MailboxService.mjs
ai/mcp/server/memory-core/services/PermissionService.mjs ai/services/memory-core/PermissionService.mjs
ai/mcp/server/memory-core/services/WakeSubscriptionService.mjs ai/services/memory-core/WakeSubscriptionService.mjs
ai/mcp/server/memory-core/services/WebhookDeliveryService.mjs ai/services/memory-core/WebhookDeliveryService.mjs
ai/mcp/server/memory-core/services/toolService.mjs ai/services/memory-core/toolService.mjs

services/lifecycle/ subdir moves (3 files; whole subdir as a unit)

Source path Destination path
ai/mcp/server/memory-core/services/lifecycle/ChromaLifecycleService.mjs ai/services/memory-core/lifecycle/ChromaLifecycleService.mjs
ai/mcp/server/memory-core/services/lifecycle/InferenceLifecycleService.mjs ai/services/memory-core/lifecycle/InferenceLifecycleService.mjs
ai/mcp/server/memory-core/services/lifecycle/SystemLifecycleService.mjs ai/services/memory-core/lifecycle/SystemLifecycleService.mjs

managers/ sibling subdir moves (4 files; whole subdir as a unit, parallel to services/ flat)

Source path Destination path
ai/mcp/server/memory-core/managers/ChromaManager.mjs ai/services/memory-core/managers/ChromaManager.mjs
ai/mcp/server/memory-core/managers/StorageRouter.mjs ai/services/memory-core/managers/StorageRouter.mjs
ai/mcp/server/memory-core/managers/AbstractVectorManager.mjs ai/services/memory-core/managers/AbstractVectorManager.mjs
ai/mcp/server/memory-core/managers/CollectionProxy.mjs ai/services/memory-core/managers/CollectionProxy.mjs

Then:

  1. Update ai/services.mjs import paths (lines 43-54) — alias names (Memory_*) preserved; only import paths change.
  2. 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).
  3. Update MC config.mjs / openapi.yaml / logger.mjs if they reference service paths.
  4. Update internal cross-references between MC services + managers (e.g., GraphService → DatabaseService internal imports; managers → AbstractVectorManager base-class imports; lifecycle services → adjacent imports).
  5. Verify memory-core-private helpers (resolveStdioIdentity, bindAgentIdentity, logIdentityStatus, logCollectionStats, logSiblingConcurrency) survive migration without import-path drift.
  6. Remove the now-empty ai/mcp/server/memory-core/services/ + ai/mcp/server/memory-core/managers/ directories.
  7. 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).

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
ai/services/memory-core/<ServiceName>.mjs paths This sub-issue + parent epic #10986 Flat SDK location for MC services + lifecycle/ + managers/ subdirs None — physical move Brief note in learn/agentos/ post-M6-epic ls ai/services/memory-core/ confirms move; old ai/mcp/server/memory-core/{services,managers}/ removed
ai/services.mjs Memory_* re-export aliases This sub-issue Continue exposing all 12 currently-aliased services + managers; only import paths change Aliases preserved → consumer code unchanged JSDoc on ai/services.mjs Operator scripts + tests run unchanged post-migration
MC Server.boot() override M2 BaseServer extension contract (#10965 / PR #10977) Custom boot order preserved (stdio-identity-resolution between services and healthcheck per #10249); only import paths inside boot() updated Migration must not change order or hook coverage MC Server.mjs JSDoc Per-server unit specs pass; identity-binding semantic verified
MC unit + integration coverage Existing test substrate Continue working post-migration with no AC delta Test fixture imports updated alongside service moves n/a All 258 MC unit specs pass (per #10977 baseline; minus 5 pre-existing Bucket G flakes unrelated to migration) + integration row green
managers/ sibling-subdir structure This sub-issue + parent epic Preserved as ai/services/memory-core/managers/ subdir under the new flat root If subdir-internal imports break, halt + investigate n/a AbstractVectorManager base-class + ChromaManager extension chain works post-migration
Memory-core-private helpers (resolveStdioIdentity, bindAgentIdentity, etc.) NL #10994 precedent + #10977 evidence Preserved as Server.mjs members; service moves don't affect Server.mjs members If broken, halt + investigate MC Server.mjs JSDoc MC stdio identity resolution + sibling-concurrency log work unchanged

Acceptance Criteria

  • AC1: All 21 MC files moved to flat SDK shape:
    • ai/services/memory-core/ — 14 files (13 services + toolService.mjs)
    • ai/services/memory-core/lifecycle/ — 3 lifecycle services
    • ai/services/memory-core/managers/ — 4 managers
    • 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.mjs boot() 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.
  • AC8: Healthcheck JSON shape unchanged (snapshot diff captured in PR evidence).
  • AC9: Neo class-system compatibility: if any MC service uses Neo.setupClass() with a className matching old path, className updated to the new path.
  • AC10: Memory-core-private helpers preserved (resolveStdioIdentity, bindAgentIdentity with #10241 retry, logIdentityStatus, logCollectionStats, logSiblingConcurrency). Stdio identity binding + sibling-concurrency log work unchanged post-migration.
  • 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).

Out of Scope

  • makeSafe() Zod schema reshape — wrapping pattern preserved as-is.
  • 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.
  • Memory-core-private helpers reshape — preserved as-is.
  • 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.

Related

  • Parent epic: #10986 — Migrate Tier-1 MCP services to flat SDK boundary (M6)
  • Sibling sub-issues (predecessors in sequence):
    • #10991 — KB migration (M6 sub-1; smallest; pattern precedent)
    • #10993 — GH-WF migration (M6 sub-2; helper subdirs precedent)
    • #10994 — NL migration (M6 sub-3; custom boot precedent)
  • Predecessor milestone: #10965 — M2 BaseServer common base class (CLOSED)
  • Sibling SDK work: #10103 — SDK-layer config-file migration (config-file dual)
  • Strategic anchor: learn/agentos/v13-path.md §3 D4 + §4 M6
  • 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.
  • Architecturally relevant prior tickets:
    • #10249 — stdio-identity-resolution-before-healthcheck rationale
    • #10145 — identity-spoof guard via beforeToolDispatch
    • #10437WakeSubscriptionService.init() pre-mcpServer
    • #10438 — fire-and-forget IIFE single-error-boundary discipline
    • #10241bindAgentIdentity retry pattern

Origin Session ID: 33ed57b5-10ed-491d-8b8f-ce5f1223ec38

Retrieval 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")

tobiu referenced in commit 4a136a4 - "refactor(memory-core): migrate services + managers + helpers to flat SDK boundary (#10996) (#11001) on May 9, 2026, 12:33 PM
tobiu closed this issue on May 9, 2026, 12:33 PM