---
id: 10994
title: Migrate neural-link services to flat SDK boundary
state: CLOSED
labels:
  - enhancement
  - ai
  - refactoring
  - architecture
assignees:
  - neo-opus-ada
createdAt: '2026-05-08T21:06:42Z'
updatedAt: '2026-05-15T12:47:35Z'
githubUrl: 'https://github.com/neomjs/neo/issues/10994'
author: neo-opus-ada
commentsCount: 0
parentIssue: 10986
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-08T23:09:11Z'
---
# Migrate neural-link services to flat SDK boundary

## Context

Filed 2026-05-08 as **M6 sub-issue 3 (neural-link)** — third per-server SDK migration in the M6 epic [#10986](https://github.com/neomjs/neo/issues/10986) sequence (KB → GH-WF → NL → MC). Sequenced after [#10991](https://github.com/neomjs/neo/issues/10991) (KB) and [#10993](https://github.com/neomjs/neo/issues/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](https://github.com/neomjs/neo/issues/10986) §The Fix and [`learn/agentos/v13-path.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/v13-path.md) §3 D4: services move from `ai/mcp/server/neural-link/services/*.mjs` → `ai/services/neural-link/*.mjs`. `ai/services.mjs` becomes a re-export aggregator with stable `<Server>_<ServiceName>` alias contracts. M2 BaseServer substrate ([#10965](https://github.com/neomjs/neo/issues/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](https://github.com/neomjs/neo/issues/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](https://github.com/neomjs/neo/pull/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](https://github.com/neomjs/neo/issues/10965) / PR [#10975](https://github.com/neomjs/neo/pull/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](https://github.com/neomjs/neo/issues/10991) (KB) and [#10993](https://github.com/neomjs/neo/issues/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](https://github.com/neomjs/neo/pull/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](https://github.com/neomjs/neo/issues/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](https://github.com/neomjs/neo/issues/10965) / PR [#10975](https://github.com/neomjs/neo/pull/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](https://github.com/neomjs/neo/pull/10975) baseline) + integration row green |
| `bridgeCwd` member + `_turnId` counter + `getCurrentTurnId()` export | NL Server.mjs preserved members from PR [#10975](https://github.com/neomjs/neo/pull/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](https://github.com/neomjs/neo/pull/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](https://github.com/neomjs/neo/pull/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](https://github.com/neomjs/neo/issues/10991)) + GH-WF ([#10993](https://github.com/neomjs/neo/issues/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](https://github.com/neomjs/neo/pull/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](https://github.com/neomjs/neo/issues/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](https://github.com/neomjs/neo/issues/10986) — Migrate Tier-1 MCP services to flat SDK boundary (M6)
- **Sibling sub-issues:**
  - [#10991](https://github.com/neomjs/neo/issues/10991) — KB migration (M6 sub-1; predecessor pattern)
  - [#10993](https://github.com/neomjs/neo/issues/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](https://github.com/neomjs/neo/issues/10965) — M2 BaseServer common base class (CLOSED)
- **Sibling SDK work:** [#10103](https://github.com/neomjs/neo/issues/10103) — SDK-layer config-file migration
- **Strategic anchor:** [`learn/agentos/v13-path.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/v13-path.md) §3 D4 + §4 M6
- **M2 per-server migration precedent:** [#10975](https://github.com/neomjs/neo/pull/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")`

## Timeline

- 2026-05-08T21:06:44Z @neo-opus-ada added the `enhancement` label
- 2026-05-08T21:06:44Z @neo-opus-ada added the `ai` label
- 2026-05-08T21:06:44Z @neo-opus-ada added the `refactoring` label
- 2026-05-08T21:06:44Z @neo-opus-ada added the `architecture` label
- 2026-05-08T21:06:45Z @neo-opus-ada added the `release:v13` label
- 2026-05-08T21:06:52Z @neo-opus-ada added parent issue #10986
- 2026-05-08T21:06:54Z @neo-opus-ada assigned to @neo-opus-ada
- 2026-05-08T21:17:50Z @neo-opus-ada cross-referenced by #10996
- 2026-05-08T21:22:46Z @neo-opus-ada cross-referenced by PR #10995
- 2026-05-08T22:02:13Z @neo-opus-ada cross-referenced by PR #10997
- 2026-05-08T22:41:09Z @neo-opus-ada cross-referenced by PR #10998
- 2026-05-08T22:53:53Z @neo-opus-ada referenced in commit `d17da57` - "fix(neural-link): update test_connection_service.mjs import path post-migration (#10994)

Tracked helper test_connection_service.mjs at ai/mcp/server/neural-link/
still imported ConnectionService via './services/ConnectionService.mjs'
which no longer resolves post-flat-SDK-migration. Updated to new path:
'../../../services/neural-link/ConnectionService.mjs'.

Caught by @neo-gpt's PR #10998 Cycle 1 review (commentId
IC_kwDODSospM8AAAABBuG1CQ). RA1 addressed.

Co-Authored-By: Claude Opus 4.7 <neo-opus-ada@neomjs.com>"
- 2026-05-08T23:09:11Z @tobiu referenced in commit `a3624f2` - "refactor(neural-link): migrate services to flat SDK boundary (#10994) (#10998)

* refactor(neural-link): migrate services to flat SDK boundary (#10994)

Migrates 9 entities from ai/mcp/server/neural-link/services/ to the
canonical ai/services/neural-link/ flat SDK boundary, mirroring the
KB (#10995) and GH-WF (#10997) M6 patterns. Updates ai/services.mjs
alias imports, NL Server.mjs custom boot() override service-imports,
and 1 test spec relocated to canonical SDK location.

Scope:
- 9 services: ComponentService, ConnectionService, DataService,
  HealthService, InstanceService, InteractionService, RecorderService,
  RuntimeService, toolService
- All 7 currently-aliased NeuralLink_* services preserved at
  ai/services.mjs (consumer-side contract unchanged); RecorderService
  + toolService remain internal
- Internal cross-references updated for new relative paths:
  - ../config.mjs → ../../mcp/server/neural-link/config.mjs
  - ../logger.mjs → ../../mcp/server/neural-link/logger.mjs
  - ../../../../../src/core/Base.mjs → ../../../src/core/Base.mjs
- toolService.mjs special-case import path fixes:
  - '../../../ToolService.mjs' → '../../mcp/ToolService.mjs'
    (resolves to ai/mcp/ToolService.mjs)
  - '../Server.mjs' (NL Server.mjs's getCurrentTurnId export per
    #10975) → '../../mcp/server/neural-link/Server.mjs'
  - path.join(__dirname, '../openapi.yaml') →
    '../../mcp/server/neural-link/openapi.yaml'
- NL Server.mjs custom boot() override imports updated for new SDK
  paths (transport-before-services semantic preserved per AC11)
- 1 unit spec (RecorderService.spec.mjs) moved from
  test/playwright/unit/ai/mcp/server/neural-link/ to
  test/playwright/unit/ai/services/neural-link/ matching new SDK shape
- Test path math: 7→6 ../ for src+ai imports + 5→4 ../ for setup.mjs
- logger.spec.mjs retained at original location (server-level, not
  service)

Custom-boot-order preservation per #10975 + #10994 AC11: NL Server.mjs
boot() override transport-before-services semantic + bridgeCwd member +
_turnId counter + getCurrentTurnId() export all preserved post-migration.

Co-Authored-By: Claude Opus 4.7 <neo-opus-ada@neomjs.com>

* fix(neural-link): update test_connection_service.mjs import path post-migration (#10994)

Tracked helper test_connection_service.mjs at ai/mcp/server/neural-link/
still imported ConnectionService via './services/ConnectionService.mjs'
which no longer resolves post-flat-SDK-migration. Updated to new path:
'../../../services/neural-link/ConnectionService.mjs'.

Caught by @neo-gpt's PR #10998 Cycle 1 review (commentId
IC_kwDODSospM8AAAABBuG1CQ). RA1 addressed.

Co-Authored-By: Claude Opus 4.7 <neo-opus-ada@neomjs.com>

---------

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-08T23:09:11Z @tobiu closed this issue
- 2026-05-09T10:14:17Z @neo-opus-ada cross-referenced by #11005
- 2026-05-09T10:42:52Z @neo-opus-ada cross-referenced by PR #11007
- 2026-05-09T11:22:25Z @neo-gemini-pro cross-referenced by #10986
- 2026-05-11T18:39:04Z @neo-gpt cross-referenced by #10103
- 2026-05-11T21:42:23Z @neo-opus-ada cross-referenced by PR #11232
- 2026-05-12T02:09:12Z @neo-gemini-pro removed the `release:v13` label

