LearnNewsExamplesServices
Frontmatter
id10994
titleMigrate neural-link services to flat SDK boundary
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-4-7
createdAtMay 8, 2026, 11:06 PM
updatedAtMay 15, 2026, 2:47 PM
githubUrlhttps://github.com/neomjs/neo/issues/10994
authorneo-opus-4-7
commentsCount0
parentIssue10986
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 9, 2026, 1:09 AM

Migrate neural-link services to flat SDK boundary

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

Context

Filed 2026-05-08 as M6 sub-issue 3 (neural-link) — third per-server SDK migration in the M6 epic #10986 sequence (KB → GH-WF → NL → MC). Sequenced after #10991 (KB) and #10993 (GH-WF) per parent epic linear-cadence rule: each migration touches ai/services.mjs import paths, so concurrent PRs would merge-conflict at the SDK aggregator.

Per parent M6 epic #10986 §The Fix and learn/agentos/v13-path.md §3 D4: services move from ai/mcp/server/neural-link/services/*.mjsai/services/neural-link/*.mjs. ai/services.mjs becomes a re-export aggregator with stable <Server>_<ServiceName> alias contracts. M2 BaseServer substrate (#10965) provides the extension contract — but neural-link is the sub-issue with the LARGEST per-server complexity gap from the canonical pattern (see Architectural Reality below).

The Problem

Neural-link services currently live under ai/mcp/server/neural-link/services/ — 9 service files. 7 are exposed via ai/services.mjs's aliasing layer (NeuralLink_ComponentService, NeuralLink_ConnectionService, NeuralLink_DataService, NeuralLink_HealthService, NeuralLink_InstanceService, NeuralLink_InteractionService, NeuralLink_RuntimeService). 2 services (RecorderService, toolService) are internal — not currently SDK-exported.

The friction (per parent epic #10986 framing): same as KB and GH-WF — the on-disk shape conflates "service implementation" with "MCP transport endpoint". This sub-issue normalizes neural-link to the existing ai/services/ConceptService.mjs flat-shape precedent.

Distinct from KB and GH-WF migrations: neural-link's Server.mjs uses a custom boot() override (per #10975 M2 migration — transport-before-services bootstrap order rationale) rather than the canonical getDependentServices() extension hook. Migration must update import paths inside the boot() override AND any imports of services like ConnectionService that NL bootstraps explicitly — broader Server.mjs surface than KB/GH-WF.

The Architectural Reality

Current state (verified 2026-05-08 via ls ai/mcp/server/neural-link/services/ + grep on ai/services.mjs):

File SDK alias (ai/services.mjs) SDK-exported
ComponentService.mjs NeuralLink_ComponentService
ConnectionService.mjs NeuralLink_ConnectionService
DataService.mjs NeuralLink_DataService
HealthService.mjs NeuralLink_HealthService
InstanceService.mjs NeuralLink_InstanceService
InteractionService.mjs NeuralLink_InteractionService
RuntimeService.mjs NeuralLink_RuntimeService
RecorderService.mjs (not exported — internal)
toolService.mjs (not exported — MCP tool router)

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

  • NL Server.mjs reduced 212 → 149 LOC during M2 migration
  • Custom boot() override preserves transport-before-services order (rationale: connect MCP transport early so client handshake succeeds even when Bridge is down)
  • Hooks used: getServerMetadata, getToolService, custom boot() override that sequences loadCustomConfig → mcpServer → connectTransport → ConnectionService.ready (non-fatal) → healthcheck
  • Member preserved: bridgeCwd (for ConnectionService.cwd assignment)
  • Module-level: _turnId counter + getCurrentTurnId() export (per-dispatch increment)

makeSafe() Zod-wrapping (ai/services.mjs:231-237): wraps all 7 SDK-exported NL services. Migration preserves wrapping; only import paths change above the wrap.

NeuralLink_Config import at ai/services.mjs:69 — handled separately from service imports; migration doesn't touch config (out of scope per parent epic).

Existing flat-shape precedent: ai/services/ConceptService.mjs + sibling sub-issues #10991 (KB) and #10993 (GH-WF) establish the per-server pattern.

The Fix

Per-file moves (9 service files):

Source path Destination path
ai/mcp/server/neural-link/services/ComponentService.mjs ai/services/neural-link/ComponentService.mjs
ai/mcp/server/neural-link/services/ConnectionService.mjs ai/services/neural-link/ConnectionService.mjs
ai/mcp/server/neural-link/services/DataService.mjs ai/services/neural-link/DataService.mjs
ai/mcp/server/neural-link/services/HealthService.mjs ai/services/neural-link/HealthService.mjs
ai/mcp/server/neural-link/services/InstanceService.mjs ai/services/neural-link/InstanceService.mjs
ai/mcp/server/neural-link/services/InteractionService.mjs ai/services/neural-link/InteractionService.mjs
ai/mcp/server/neural-link/services/RecorderService.mjs ai/services/neural-link/RecorderService.mjs
ai/mcp/server/neural-link/services/RuntimeService.mjs ai/services/neural-link/RuntimeService.mjs
ai/mcp/server/neural-link/services/toolService.mjs ai/services/neural-link/toolService.mjs

Then:

  1. Update ai/services.mjs import paths (lines 62-68) — alias names (NeuralLink_*) preserved; only import paths change.
  2. Update ai/mcp/server/neural-link/Server.mjs custom boot() override — import paths for any services referenced inside boot() (e.g., ConnectionService.ready call); also update any other service imports the NL Server uses.
  3. Update NL config.mjs / openapi.yaml if they reference service paths.
  4. Update internal cross-references between NL services.
  5. Verify bridgeCwd member assignment + _turnId counter + getCurrentTurnId() export still work post-migration.
  6. Remove the now-empty ai/mcp/server/neural-link/services/ directory.
  7. Verify operator scripts work unchanged via the preserved NeuralLink_* aliases.

Class names UNCHANGED. Service file names UNCHANGED. Only physical locations change.

Neo class-system compatibility note: Same as KB/GH-WF — verify during implementation whether any NL service uses Neo.setupClass() with className matching old path. NL services likely follow factory-singleton pattern.

Custom-boot-order preservation: NL's boot() override exists to preserve transport-before-services semantic (rationale captured in PR #10975). Migration must NOT change the boot order — only the import paths inside the override.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
ai/services/neural-link/<ServiceName>.mjs paths This sub-issue + parent epic #10986 Flat SDK location for NL services None — physical move Brief note in learn/agentos/ post-M6-epic ls ai/services/neural-link/ confirms move; old ai/mcp/server/neural-link/services/ removed
ai/services.mjs NeuralLink_* re-export aliases This sub-issue Continue exposing all 7 currently-aliased services; only import paths change Aliases preserved → consumer code unchanged JSDoc on ai/services.mjs Operator scripts + tests run unchanged post-migration
NL Server.boot() override M2 BaseServer extension contract (#10965 / PR #10975) Custom boot order preserved; only import paths inside boot() updated Migration must not change order — transport-before-services rationale per #10975 NL Server.mjs JSDoc Per-server unit specs pass; transport-before-services semantic verified
NL unit + integration coverage Existing test substrate Continue working post-migration with no AC delta Test fixture imports updated alongside service moves n/a All 7 existing NL unit specs pass (per #10975 baseline) + integration row green
bridgeCwd member + _turnId counter + getCurrentTurnId() export NL Server.mjs preserved members from PR #10975 Preserved post-migration; service moves don't affect Server.mjs members If broken, halt + investigate NL Server.mjs JSDoc NL CallTool dispatch increments turn counter as before

Acceptance Criteria

  • AC1: All 9 NL service files moved from ai/mcp/server/neural-link/services/ to ai/services/neural-link/. Old directory removed.
  • AC2: ai/services.mjs continues exporting all 7 currently-aliased NL services (NeuralLink_ComponentService, NeuralLink_ConnectionService, NeuralLink_DataService, NeuralLink_HealthService, NeuralLink_InstanceService, NeuralLink_InteractionService, NeuralLink_RuntimeService); only import paths change.
  • AC3: ai/mcp/server/neural-link/Server.mjs boot() override updated to import services from new SDK paths. Custom transport-before-services boot order PRESERVED.
  • AC4: Operator scripts + daemons + tests consume NL services unchanged (no code changes required at consumer sites; aliases hold).
  • AC5: Internal cross-references between NL services updated.
  • AC6: All 7 NL unit specs pass post-migration (per #10975 PR4 evidence baseline).
  • AC7: Integration row green post-migration on the PR.
  • AC8: Healthcheck JSON shape unchanged.
  • AC9: Neo class-system compatibility: if any NL service uses Neo.setupClass() with className matching old path, className updated.
  • AC10: bridgeCwd member + _turnId counter + getCurrentTurnId() export semantics preserved post-migration. CallTool dispatch turn counter increments as before.
  • AC11: Transport-before-services boot semantic preserved (verifiable via boot order in boot() override + per-spec assertion).
  • AC12: PR description includes a brief diff summary table.

Out of Scope

  • makeSafe() Zod schema reshape — wrapping pattern preserved as-is.
  • Renaming NL services or class names — physical move only.
  • Reshaping NL's custom boot() order — preserved as-is per PR #10975 rationale; this sub-issue does NOT revisit transport-before-services architectural decision.
  • bridgeCwd member or _turnId counter reshape — preserved as-is.
  • Touching ai/mcp/server/shared/services/* — shared cross-cutting services unaffected.
  • Other Tier-1 servers — KB (#10991) + GH-WF (#10993) + memory-core (to be filed) migrations are separate sub-issues.
  • <Server>_<ServiceName> namespace prefix collapse — preserved through M6 per parent epic Out-of-Scope.
  • Promoting RecorderService / toolService to SDK-exported — they're internal for a reason (NL recorder is internal to the harness substrate; toolService is the MCP tool-routing surface).
  • ai/services.mjs re-organization — this sub-issue updates NL import paths only.

Avoided Traps / Gold Standards Rejected

  • Rejected: only move SDK-exported services, leave internal services in old location. Splits NL services across two top-level dirs categorized by export-status.
  • Rejected: rename NeuralLink_* aliases to drop prefix during migration. Breaks consumers.
  • Rejected: refactor makeSafe Zod wrapping pattern alongside the move. Increases PR risk.
  • Rejected: convert NL's custom boot() to canonical getDependentServices() flow. Architectural reshape; rationale for custom order is preserved per PR #10975 — transport-before-services is load-bearing for client-handshake during Bridge down/spawning. Migration is path-only.
  • Rejected: parallel cadence with sibling M6 sub-issues (KB / GH-WF / MC). Per parent epic #10986 Avoided Traps: linear sequencing keeps ai/services.mjs under single-edit lock.
  • Rejected: gradual migration via indirection layer. Clean cut-over simpler for 9-file scope.
  • Rejected: split NL migration into "services" + "boot-order-imports" PRs. Both touch same ai/services.mjs block; combining is cleaner. The boot() override import-path update is small (verify at implementation time).

Related

  • Parent epic: #10986 — Migrate Tier-1 MCP services to flat SDK boundary (M6)
  • Sibling sub-issues:
    • #10991 — KB migration (M6 sub-1; predecessor pattern)
    • #10993 — GH-WF migration (M6 sub-2; immediate predecessor in sequence)
    • memory-core (M6 sub-4; to be filed; last because most complex)
  • Predecessor milestone: #10965 — M2 BaseServer common base class (CLOSED)
  • Sibling SDK work: #10103 — SDK-layer config-file migration
  • Strategic anchor: learn/agentos/v13-path.md §3 D4 + §4 M6
  • M2 per-server migration precedent: #10975 — neural-link migration to BaseServer + introduced boot() overridable seam. The same Server.mjs that received the boot() seam now receives M6's import-path update inside that override.

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

Retrieval Hint: query_raw_memories(query="M6 SDK migration neural-link services flat shape ai/services/neural-link NeuralLink_ aliases boot override transport-before-services")

tobiu referenced in commit a3624f2 - "refactor(neural-link): migrate services to flat SDK boundary (#10994) (#10998) on May 9, 2026, 1:09 AM
tobiu closed this issue on May 9, 2026, 1:09 AM