LearnNewsExamplesServices
Frontmatter
id13336
titleFleet Manager: turnkey stopAgent + restartAgent on the facade
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-ada
createdAtJun 15, 2026, 1:14 PM
updatedAtJun 15, 2026, 2:03 PM
githubUrlhttps://github.com/neomjs/neo/issues/13336
authorneo-opus-ada
commentsCount0
parentIssue13015
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 15, 2026, 2:03 PM

Fleet Manager: turnkey stopAgent + restartAgent on the facade

Closed v13.1.0/archive-v13-1-0-chunk-3 enhancementaiarchitecture
neo-opus-ada
neo-opus-ada commented on Jun 15, 2026, 1:14 PM

Context

Sub-leaf of #13015 (Fleet Manager MVP, pillar 1 of #13012). The FleetManager control-plane facade (ai/services/fleet/FleetManager.mjs, shipped via #13192) is the surface-independent service layer that resolves managedRoot once and exposes operator operations turnkey, so any surface (an MCP control-plane, the settings pane) sits thinly on top. Today it exposes only startAgent + fleetRepoStatus — the mutating lifecycle verbs stop / restart are not turnkey on the facade, so a consuming surface would have to reach into FleetLifecycleService directly, defeating the facade's single-turnkey-surface purpose. #13015's MVP success criterion names "whole-fleet start/stop/restart/health".

The Problem

The lifecycle primitive already implements stop + restart + status — FleetLifecycleService.stop(id) (:217), async restart(id) (:256), status(id) (:266) — but the facade only surfaces startAgent. The gap is pure exposure: the operator-operations surface is incomplete (start-only), so the downstream MCP-control-plane / settings-pane leaves cannot stop or restart an agent through the one turnkey layer.

The Architectural Reality

  • ai/services/fleet/FleetManager.mjs:122startAgent delegates to startAgentProvisioned (provision-then-start); fleetRepoStatus delegates to inspectFleetRepos. The facade idiom: resolve managedRoot + the lifecycle service, delegate to a merged primitive.
  • ai/services/fleet/FleetLifecycleService.mjs:256restart calls start(id) with NO cwd → a Fleet-Manager-provisioned agent (started via startAgentProvisioned with cwd = repoPath) would re-spawn in the FM's own directory, not its repo. That re-introduces the path-keyed-memory correctness bug fixed for the start path in #13177. So a turnkey restartAgent MUST route through the provisioned start, not lifecycleService.restart.
  • ai/services/fleet/removeAgentRepo.mjs:28-32 — explicitly states the repo-removal primitive is "deliberately never auto-wired into removeAgent"; removal + auto-memory reconciliation is the blocked #13190 (needs a Memory-Core delete/archive/tombstone policy). So removeAgent is correctly OUT of this leaf.

The Fix

In ai/services/fleet/FleetManager.mjs, add two turnkey methods mirroring the existing delegation idiom:

  • stopAgent(id)this.getLifecycleService().stop(id) — thin delegation; returns {success, id, state}.
  • restartAgent(id)await this.stopAgent(id); return this.startAgent(id)provisioned restart: stop, then the facade's own provision-then-start (re-ensures the repo via ensureAgentRepo + preserves cwd). Deliberately NOT a delegation to lifecycleService.restart (which drops cwd).

Unit tests added to test/playwright/unit/ai/FleetManager.spec.mjs, mirroring the existing inject-lifecycle-stub + assert-delegation pattern.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
FleetManager.stopAgent(id) (new) this leaf delegate → lifecycleService.stop(id){success,id,state} n/a JSDoc @summary unit: delegation + id passthrough
FleetManager.restartAgent(id) (new) this leaf await this.stopAgent(id) then this.startAgent(id) (provisioned, cwd-preserving) n/a JSDoc @summary unit: stop-then-provisioned-start
FleetLifecycleService.stop(id) existing #13049 (:217) consumed as-is existing read FleetLifecycleService.mjs:217
FleetLifecycleService.restart(id) existing #13049 (:256) NOT consumed (drops cwd) provisioned restart instead existing read FleetLifecycleService.mjs:256

Decision Record impact

none — facade method additions consistent with the established startAgent / fleetRepoStatus delegation pattern; no ADR authority interaction.

Acceptance Criteria

  • FleetManager.stopAgent(id) delegates to the (injected) lifecycle service's stop with the agent id and returns its result.
  • FleetManager.restartAgent(id) stops then re-starts via the provisioned path (startAgent), so a provisioned agent restarts in ITS repo cwd (not the FM dir) — verified by asserting the provision-then-start composer is invoked on restart.
  • Unit tests mirror FleetManager.spec.mjs (serial, injected-field reset, delegation assertions); full unit suite green.
  • JSDoc @summary on both methods (Anchor & Echo).

Out of Scope

  • removeAgent / repo removal / auto-memory reconciliation — the blocked #13190 (needs a Memory-Core policy + honors removeAgentRepo's never-auto-wire contract).
  • Per-agent process status passthrough on the facade — additive, can follow if a consuming surface needs it.
  • The operator-facing surface itself (MCP control-plane / settings-pane wiring) — a separate leaf that consumes this facade (per FleetManager.mjs JSDoc).
  • Fixing FleetLifecycleService.restart's cwd-drop at the primitive layer — restartAgent routes around it; a primitive-level fix (or JSDoc caveat) can be a separate follow-up.

Avoided Traps

  • Delegating restartAgentlifecycleService.restart — rejected: drops cwd, re-spawning a provisioned agent in the FM dir not its repo (the #13177-class path-keyed-memory bug). The provisioned restart is the correct turnkey shape.
  • Auto-wiring removeAgentremoveAgentRepo — rejected: the primitive's contract forbids it; removal is the blocked #13190 policy.

Related

Release classification: post-release (product line; nothing v13.x-blocking — inherits #13015).

Live latest-open sweep: checked latest 25 open issues + 30 recent A2A claims immediately before filing; no equivalent FM-facade-lifecycle ticket or in-flight claim.

Authored by Claude Opus 4.8 (Claude Code), @neo-opus-ada. Origin Session ID: 73156d71-9a96-4bf1-bbc8-d6487ca7dddd Retrieval Hint: "Fleet Manager facade turnkey stopAgent restartAgent provisioned restart cwd lifecycle"