LearnNewsExamplesServices
Frontmatter
id11005
titleAlign Neo classNames with flat SDK locations (M6 follow-up sweep)
stateClosed
labels
enhancementairefactoringarchitecturemodel-experience
assigneesneo-opus-4-7
createdAtMay 9, 2026, 12:14 PM
updatedAtMay 12, 2026, 4:09 AM
githubUrlhttps://github.com/neomjs/neo/issues/11005
authorneo-opus-4-7
commentsCount2
parentIssue10986
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 9, 2026, 1:18 PM

Align Neo classNames with flat SDK locations (M6 follow-up sweep)

Closedenhancementairefactoringarchitecturemodel-experience
neo-opus-4-7
neo-opus-4-7 commented on May 9, 2026, 12:14 PM

Context

Filed 2026-05-09 after @tobiu caught a substrate-discipline failure on PR #11001 Cycle review (MC migration sub-4 of M6 epic #10986): all 4 M6 sub-issues (#10991 KB, #10993 GH-WF, #10994 NL, #10996 MC) physically moved service classes from ai/mcp/server/<server>/services/ to the flat ai/services/<server>/ SDK boundary BUT did not update the corresponding Neo.setupClass() className strings.

Result: the four M6-migrated server families now have systemic drift between file location and Neo class registry namespace:

  • File at ai/services/memory-core/GraphService.mjs
  • className still Neo.ai.mcp.server.memory-core.services.GraphService

Operator's framing: "if you did not update neo classNames, inside the migration, this is an utter failure and requires new tickets."

The cited rationale on M6 sub-issues — "Renaming MC services or class names — physical move only" — was misread. "Physical move only" was meant to constrain the migration to physical relocation including namespace alignment, not to forbid namespace updates. Per Neo conventions (@class Neo.<path> mirrors file path; KB indexing, Native Edge Graph hierarchy traversal, and class-introspection tooling all rely on this mirror), a physical move without namespace update violates the convention.

The Problem

54-ish className strings across 4 server families now lie about their location. Concrete fallout:

  1. Knowledge Base indexingmcp__neo-mjs-knowledge-base__get_class_hierarchy and query_documents rely on className-to-file-path resolution. Stale className causes wrong-file lookups or empty results.
  2. Native Edge Graph hierarchy — class-tree traversal in NEG uses className as the canonical identifier; child-class-of-X queries on the old namespace will return migrated classes that no longer live there.
  3. Agent introspection toolinginspect_class and get_method_source derive expected file paths from className; agents will fail to resolve OR resolve to the wrong path expectation.
  4. Documentation drift — every JSDoc @class Neo.ai.mcp.server.<server>.services.X and @member className=... block in the 54 affected classes is now structurally wrong. KB doc pipeline ingests these as authoritative.
  5. Future-reader confusion — humans + agents navigating from ai/services/memory-core/GraphService.mjs see className Neo.ai.mcp.server.memory-core.services.GraphService, then can't reverse-derive the new location.

The runtime DOES NOT crash because Neo's class registry uses className as a registry key (string-equality lookup), not as a path-derivation rule. So CI passed for KB + GH-WF + NL merges and currently passes for MC PR #11001. The drift is silent at runtime, loud in tooling and documentation surfaces.

The Architectural Reality

Neo's Neo.setupClass(Class) uses Class.config.className as the registry key under globalThis.Neo. Two consumers depend on className-mirrors-path:

  • Neo.create('Neo.ai.X.Y.Z') resolves the class via the registry; works regardless of file location IF the registry was populated correctly. Migration-time module loading still triggers Neo.setupClass, so the class IS in the registry. But the registry key now disagrees with the file system.
  • learn/agentos/CodebaseOverview.md + ChromaDB-indexed source content map className → file path for documentation/AI tooling. KB embeddings include @class JSDoc tags as semantic anchors. Stale anchors mis-direct semantic search.

Neo conventions per learn/guides/fundamentals/CodebaseOverview.md and core/Base.mjs JSDoc precedent: className mirrors file path. The physical migration should preserve that mirror. Sub-issues #10991/#10993/#10994/#10996 all violated it.

Affected files (per grep -h "className.*Neo\.ai" ai/services/<server>/*.mjs):

Server className count OLD prefix NEW prefix (target)
Knowledge Base ~10 Neo.ai.mcp.server.knowledge-base.services.X Neo.ai.services.knowledge-base.X
GitHub Workflow ~13 Neo.ai.mcp.server.github-workflow.services.X (+ .sync.X, .queries.X) Neo.ai.services.github-workflow.X (+ .sync.X, .queries.X)
Neural Link ~9 Neo.ai.mcp.server.neural-link.services.X Neo.ai.services.neural-link.X
Memory Core ~22 Neo.ai.mcp.server.memory-core.services.X (+ .lifecycle.X, .managers.X) Neo.ai.services.memory-core.X (+ .lifecycle.X, .managers.X)

Total: ~54 className strings + corresponding @member className=... JSDoc tags.

The Fix

Mechanical regex sweep across the 4 affected ai/services/<server>/ directories:

For each migrated service:

  1. Update className: 'Neo.ai.mcp.server.<server>.services.X'className: 'Neo.ai.services.<server>.X'
  2. Update @member {String} className='Neo.ai.mcp.server.<server>.services.X' JSDoc tag (paired)
  3. Update @class Neo.ai.mcp.server.<server>.services.X JSDoc tag (where present)
  4. Preserve sub-namespaces:
    • MC .services.lifecycle.X.lifecycle.X
    • MC .managers.X.managers.X
    • GH-WF .services.sync.X.sync.X
    • GH-WF .services.queries.X.queries.X

Per-server regex examples:

<h1 class="neo-h1" data-record-id="6">Memory Core flat services (top-level)</h1>

sed -i '' "s|Neo\.ai\.mcp\.server\.memory-core\.services\.|Neo.ai.services.memory-core.|g" ai/services/memory-core/*.mjs
<h1 class="neo-h1" data-record-id="7">(then specific sub-namespace passes for lifecycle/managers, etc.)</h1>

Recommended approach: write a one-shot buildScripts/ai/_align_classnames.mjs migration helper (mirroring the M6 migration script pattern), run it, delete in same PR.

No runtime-behavior change. The Neo class registry repopulates under the new keys at Neo.setupClass(); consumers using Neo.create('Neo.ai.X.Y.Z') need their references updated only if they explicitly hardcoded the old fully-qualified namespace (rare; the SDK-aliasing layer in ai/services.mjs is the proper consumer interface).

Acceptance Criteria

  • AC1: All ~54 className: '...' config strings updated across the 4 M6-migrated server directories (ai/services/{knowledge-base,github-workflow,neural-link,memory-core}/).
  • AC2: All paired @member {String} className='...' JSDoc tags updated to match (1:1 with AC1).
  • AC3: All @class Neo.ai.mcp.server.<server>.services.X JSDoc tags updated to @class Neo.ai.services.<server>.X form.
  • AC4: Sub-namespace structure preserved: MC .lifecycle.X + .managers.X; GH-WF .sync.X + .queries.X.
  • AC5: No runtime regressions: full npm run test-unit baseline pre/post comparison; full npm run test-integration for affected scopes; expected delta = 0 new failures.
  • AC6: Hardcoded old-namespace references in consumer code (other ai/, buildScripts/ai/, test/playwright/ directories) audited via grep -rn "Neo\.ai\.mcp\.server\.<server>\.services" — every match either updated or explicitly justified as a string-literal that survives className renames (e.g., wire-format strings, error-message templates).
  • AC7: KB index re-built post-merge (npm run ai:build-kb or equivalent) so semantic search picks up the new className anchors. Verify via ask_knowledge_base(query='GraphService class') — top result should reference Neo.ai.services.memory-core.GraphService, not the old namespace.
  • AC8: Test-spec assertion sweep: any expect(X.className).toBe('Neo.ai.mcp.server...') or similar regex-literal assertion updated (mirrors the bulk-migration regex-edge-case from PR #11001 fix 0be1f622d).
  • AC9: Native Edge Graph re-ingestion: confirm class-hierarchy queries (mcp__neo-mjs-neural-link__get_class_hierarchy) return the migrated classes under the new namespace prefix.

Out of Scope

  • Renaming the SHORT class name (e.g., GraphService → SomethingElse). Convention preserved.
  • Reshaping the SDK aliasing layer (Memory_*, KB_*, GH_*, NeuralLink_* aliases in ai/services.mjs). Aliases are stable consumer API.
  • Changing Neo.setupClass() semantics or class-registry behavior. Pure data update.
  • Consolidating the per-server namespaces (e.g., flattening memory-core and knowledge-base into a unified services namespace). Server-family identity preserved.
  • Updating the OLD Neo.ai.mcp.server.<server> namespace in non-migrated code (config.mjs, logger.mjs, openapi.yaml, Server.mjs, shared/ helpers — these stay at server level and keep the original namespace).

Avoided Traps / Gold Standards Rejected

  • Rejected: hold M6 epic closeout until className sweep lands. The four physical migrations are independently shippable; merging them without className alignment is a known-narrow gap that this ticket explicitly captures. M6 closes on physical-move-complete.
  • Rejected: retroactively patch className updates into PR #11001 (open at filing time). Scope-creep during cross-family review; would force re-review on a now-clean PR. Better to merge #11001 as-is (consistent with KB+GH-WF+NL precedent) and close the systemic gap in this dedicated follow-up.
  • Rejected: per-server follow-up tickets (4 separate sub-issues). Single-ticket scope is mechanical regex sweep across 4 dirs; splitting creates artificial coordination overhead without granularity benefit.
  • Rejected: fold this into the v13 release-gate ticket #11003. That's a Dockerized remote MCP transport proof; mixing a cosmetic-namespace sweep with a release-gate test bloats both.

Related

  • Parent epic: #10986 — Migrate Tier-1 MCP services to flat SDK boundary (M6). Drift originates here. Linked as Related rather than sub-issue, since M6 closes on the physical migrations regardless.
  • Migrated PRs that introduced the drift:
  • Strategic anchor: learn/agentos/v13-path.md §3 D4 + §4 M6
  • Convention reference: learn/guides/fundamentals/CodebaseOverview.md (className-mirrors-file-path); src/core/Base.mjs setupClass registry-key behavior

Origin Session ID: c2912891-b459-4a03-b2af-154d5e264df1

Retrieval Hint: query_raw_memories(query="M6 SDK migration className drift Neo.ai.mcp.server.services flat namespace alignment KB GH-WF NL MC follow-up sweep")

tobiu referenced in commit 54600d7 - "refactor(ai-services): align Neo classNames with flat SDK locations across M6 servers (#11005) (#11007) on May 9, 2026, 1:18 PM
tobiu closed this issue on May 9, 2026, 1:18 PM