LearnNewsExamplesServices
Frontmatter
id16679
titleDissolve src/ai/fleet — FM vocabulary moves to its owning realms
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-fable-clio
createdAtAug 8, 2026, 1:46 PM
updatedAtAug 8, 2026, 3:35 PM
githubUrlhttps://github.com/neomjs/neo/issues/16679
authorneo-fable-clio
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 8, 2026, 3:35 PM
milestonev13.2

Dissolve src/ai/fleet — FM vocabulary moves to its owning realms

Closed Backlog/active-chunk-13 enhancementairefactoringarchitecture
neo-fable-clio
neo-fable-clio commented on Aug 8, 2026, 1:46 PM

Priority: HIGH (operator-directed, 2026-08-08) — the debt is measured to be actively compounding: PR #16675 added two fresh ai/ → src/ai/fleet import edges today (see The Problem). Every week of deferral raises the relocation cost.

Context

Operator challenge (2026-08-08, verbatim core): "src/ai/fleet is pure debt... neo has the body => src folder => frontend realm and core, meant to run as ESM inside browsers. and there is the brain => ai folder => nodejs scripts... we have apps/agentos as the home for the FM frontend. we did NOT name it fleet on purpose, since it will get enhanced to an agent harness (v14 project home), as well as showing diagnostics for the agent os... right now, the neo body contains mcp server definitions inside the fleet folder, and this just feels wrong."

The steward verification sweep (same day) confirmed the debt and sharpened its class; the design disposition is on public record as a post-fold delta note on D#16652 (DC_kwDODSospM4BEcpX): a module can be plane-clean and category-wrong. These six files pass D#16652's converged closure doctrine (pure, store-free, genuinely dual-consumed — a guard would derive them shared and stay green) and are still misplaced on the axis that doctrine deliberately does not govern: what the code IS (@neo-opus-grace's axis distinction, D#16652 fold). The engine tree (src/ — what browsers execute as the framework) is carrying Agent-OS product vocabulary.

The Problem

Inventory (measured 2026-08-08, session-verified):

src/ai/fleet/mcpServers.mjs               191 loc   MCP catalog + sparse-override validation + REMOTE_MCP_CREDENTIAL_ENV_VAR
src/ai/fleet/fleetCockpitStatus.mjs       287 loc   cockpit status envelope constructors + source registry
src/ai/fleet/harnessTypes.mjs              52 loc   harness-type registry (defineAgent validation + picker labels)
src/ai/fleet/fleetWireMethods.mjs          72 loc   the bridge wire-method contract list
src/ai/fleet/installFleetBridge.mjs       148 loc   browser boot glue (apps/agentos/app.mjs installs it)
src/ai/fleet/createFleetRegistryBridge.mjs 38 loc   browser bridge factory
                                          788 loc
  • All six are transitively dual-consumed: browser side via apps/agentos/app.mjs boot + view modules + two apps/agentos/config/ thin re-export fronts; node side via 7+ ai/services/fleet/* + ai/scripts/fleet/* importers.
  • The accretion is live: PR #16675 (open, 2026-08-08) adds import {REMOTE_MCP_CREDENTIAL_ENV_VAR} from '../../../src/ai/fleet/mcpServers.mjs' into ai/mcp/client/Client.mjs + ai/mcp/client/config.mjs. The author did the right thing under the current layout — importing the one authority beats duplicating a credential env-var name. The layout makes correct behavior grow the debt: a credential env-var name for the Brain's canonical MCP client now lives in the Body engine tree.
  • The app side already signals the correct ownership: apps/agentos/config/mcpServers.mjs + config/harnessTypes.mjs exist purely as re-export fronts "so app views keep their conventional config import path" — the product surface already wants this vocabulary as app config.
  • The v14 trajectory (operator, same challenge): apps/agentos is deliberately named for what it becomes — the agent-harness project home + Agent OS diagnostics surface. Its vocabulary belongs to it, not to the engine namespace every framework consumer reads.

The Architectural Reality

  • Realm doctrine (ADR 0018, two hemispheres): src/ = Body engine (browser ESM); ai/ = Brain (Node). src/ai/* has one legitimate citizen class: the extended-NL Body-side organs that RUN in the App-Worker (WriteGuard, LockRegistry, TransactionService, client/* — the #13056 in-heap half). The fleet/ subfolder is not that class; it is FM product contract vocabulary that rode in on the folder name.
  • The boundary that must not invert (D#16652 fold, measured): src → ai = 0 (lint-checkable), ai → src = 325. This ticket REDUCES the ai→src edge count and touches the src→ai invariant not at all.
  • The proven parity pattern: this repo already solves same-vocabulary-two-surfaces without shared imports — ai/scripts/lint/lint-config-template-ssot.mjs (config-leaf-parity snapshot) and ai/scripts/lint/lint-openapi-service-parity.mjs. Drift = red CI, authority stays single.
  • Operable-cold (the #14560 product principle): the cockpit renders labels/pickers/state names with the fleet server down (#15284's banner). A wire-only contract would break this; the app-side render vocabulary must stay importable in the browser.
  • The wire already exists: FleetControlBridge over the authenticated fleet HTTP ingress (#15320, single-viewer) serves the full catalog at runtime; the browser needs local vocabulary, not local authority.

The Fix

Dissolve src/ai/fleet/ entirely. Realm assignments (exact filenames finalized in the PR under structural-pre-flight; both targets have exact sibling precedents):

  1. Authority → ai/services/fleet/ (48-file owning folder, structure-map-verified): the MCP catalog + sparse-override validation + REMOTE_MCP_CREDENTIAL_ENV_VAR, the harness-type registry (validation half), the status-envelope constructors, and the wire-method list's validating side. The services that validate the vocabulary own it.
  2. Render vocabulary → apps/agentos/config/: the existing thin fronts become the real files — labels, picker entries, state names ONLY; zero validation logic. This is the operable-cold local surface and the v14-trajectory home.
  3. Browser boot glue → apps/agentos/: installFleetBridge + createFleetRegistryBridge are FM-product boot modules booted by apps/agentos/app.mjs; they move to the app that boots them (exact placement per structural-pre-flight; their unit specs move along).
  4. Parity lint binds every dual vocabulary (catalog keys/labels/defaults, harness types, wire methods, status states) in the proven snapshot style: one authority, one render copy, drift is red.
  5. All importers updated (~25 sites measured across ai/, apps/, test/). PR #16675 coordination: whichever merges first, the other rebases one import path — no serialization, coordinated with @neo-gpt via A2A.

Contract Ledger Matrix

Target Surface Source of Authority (today → after) Proposed Behavior Fallback Docs Evidence
MCP_SERVERS catalog + sparse-override validation + REMOTE_MCP_CREDENTIAL_ENV_VAR src/ai/fleet/mcpServers.mjsai/services/fleet/ module Values byte-identical; node importers repoint; browser reads labels from apps/agentos/config/ none needed (mechanical move) JSDoc names the parity contract on both sides parity snapshot + unit scope green
Harness-type registry src/ai/fleet/harnessTypes.mjs → split per above defineAgent/configureAgent validate Brain-side; pickers render app-side none same same
Cockpit status envelopes src/ai/fleet/fleetCockpitStatus.mjs → constructors Brain-side; state-name vocabulary app-side Wire carries instances (already does); app consumers (sourceHealth, nameSlot) read app-side vocabulary none same cockpit boots live + cold
Wire-method contract list src/ai/fleet/fleetWireMethods.mjs → authority Brain-side; browser stub list app-side dispatchFleetRequest validates against authority; bridge builds proxies from app-side list; parity lint binds the two none same parity lint red-proven once
Bridge boot glue src/ai/fleet/installFleetBridge.mjs + createFleetRegistryBridge.mjsapps/agentos/ Unchanged behavior; specs move none same unit scope green

Decision Record impact

aligned-with ADR 0018 (two-hemisphere realm doctrine). Informs D#16652's pending Decision Record: the category axis (what code is) is distinct from the executability axis (where a process runs) — recorded there at DC_kwDODSospM4BEcpX. This ticket is deliberately not a graduation of D#16652 (independent premise: operator directive + measured accretion; scope disjoint from the barrel/plane fold).

Acceptance Criteria

  • src/ai/fleet/ no longer exists; grep -rn "src/ai/fleet" src/ apps/ ai/ test/ buildScripts/ → 0 hits (archived content exempt).
  • src/ai/ contains only the extended-NL Body-side organs (WriteGuard, LockRegistry, TransactionService, client/*, admission helpers) — zero FM product vocabulary.
  • Authority modules land under ai/services/fleet/ with JSDoc naming the parity contract; render vocabulary under apps/agentos/config/ with zero validation logic.
  • A parity lint binds every dual vocabulary; proven red once against an induced drift in the PR's test evidence, then green.
  • All importers repointed; PR #16675's two imports covered regardless of merge order (A2A-coordinated with @neo-gpt).
  • Catalog VALUES byte-identical pre/post (no behavior change; the parity snapshot is the proof).
  • Fleet unit scope green (493-spec scope + moved specs); cockpit verified live AND operable-cold (banner renders with the server down).

Out of Scope

  • The D#16652 host/cloud SDK barrel split (Option B) — different axis, its own Decision Record, its own cut.
  • #16645 (devFleetServer env re-derivation beside the leaves) — adjacent ADR-0019 cleanup, separately claimed.
  • Any CHANGE to vocabulary content (labels, defaults, keys) — this is relocation, byte-identical.
  • The v14 diagnostics/harness surfaces the operator pictures for apps/agentos — this ticket clears their ground, nothing more.

Avoided Traps

  1. apps/ as authorityai/ services importing from apps/ inverts ownership (services outlive UIs); rejected.
  2. A new shared/ tree — D#16652's A-row lesson: a second importable place is a second thing to import wrongly; and inventing a third realm has zero sibling precedent. Rejected.
  3. Wire-only (no local vocabulary) — breaks operable-cold (#14560 product principle, #15284's banner). Rejected.
  4. Keep src/ai/fleet + add a guard — the G-closure doctrine would derive these files shared and stay green forever; the category error would be legalized, and the engine tree would keep accreting product vocabulary behind a green guard. Rejected — this is the exact failure mode the D#16652 delta note warns the Decision Record about.
  5. The npm-boundary argument — self-falsified during verification (.npmignore ships ai/ too); deliberately NOT load-bearing here, recorded so no successor re-derives it.

Creation-gate record

Live latest-open sweep: latest 20 open issues checked 2026-08-08T11:42Z — no equivalent (nearest neighbor #16645, adjacent-distinct, linked). Local archive grep: mentions only, no relocation ticket. A2A in-flight sweep: Memory Core unavailable at create time (container flap); equivalence: this claim's earliest durable timestamps are 11:24Z (D#16652 delta note announcing "ticket follows the standard gates") + 11:25Z (all-agents broadcast) — any competing claim post-dates both and resolves via first-claim-timestamp-wins. Structure-map gate: ai/services/fleet (48 files) confirmed owning folder; app-side siblings are the existing apps/agentos/config/ fronts.

Related

  • #14560 (FM cockpit epic — steward context) · #13015 (FM service spine)
  • PR #16675 (the live accretion example; coordination, not blockage)
  • #16645 (adjacent devFleetServer config cleanup, same file neighborhood)
  • D#16652 (the plane doctrine this aligns beside; delta note DC_kwDODSospM4BEcpX)

Origin Session ID: b0aa9931-78c8-4723-bd11-683b99bd3a36 Retrieval Hint: "src/ai/fleet dissolution parity lint realm placement category axis"

tobiu referenced in commit 656673e - "chore: dissolve src/ai/fleet — FM vocabulary to its owning realms (#16679) (#16687) on Aug 8, 2026, 3:35 PM
tobiu closed this issue on Aug 8, 2026, 3:35 PM