LearnNewsExamplesServices
Frontmatter
id15048
titleBootstrap managed peer workspaces before Fleet launch
stateClosed
labels
enhancementdeveloper-experienceaiarchitecturebuildmodel-experience
assigneesneo-gpt-emmy
createdAtJul 12, 2026, 12:34 AM
updatedAt4:29 PM
githubUrlhttps://github.com/neomjs/neo/issues/15048
authorneo-gpt
commentsCount2
parentIssue13015
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAt4:29 PM
milestonev13.2

Bootstrap managed peer workspaces before Fleet launch

Closed Backlog/active-chunk-5 enhancementdeveloper-experienceaiarchitecturebuildmodel-experience
neo-gpt
neo-gpt commented on Jul 12, 2026, 12:34 AM

Context

The first real @neo-gpt-emmy setup proved that Fleet's current “repo provisioned” state is necessary but not sufficient. The checkout existed, yet a maintainer still had to hydrate ignored AI configs, install the canonical .neo-ai-data links, preserve clone-local daemon/process directories, create Codex project/home configuration, enable isolated Markdown memory, and verify that no state linked back to Euclid.

The shipped start composer stops one step early: startAgentProvisioned() performs clone-or-reuse and immediately calls lifecycleService.start(..., {cwd}) (ai/services/fleet/startAgentProvisioned.mjs:42-75). The existing bootstrap module already exposes an import-safe hydrateCurrentWorktree() that hydrates AI overlays, data links/read aliases, the Sandman handoff, and Claude settings without running the CLI (bootstrapWorktree.mjs:950-973); the CLI wrapper itself always runs build-all (lines 1229-1231) and is therefore the wrong Fleet boundary.

The Problem

A freshly cloned Fleet checkout can be launched while still missing the local state that makes it a distinct, correctly wired resident:

  • ignored ai/config.mjs / MCP config overlays;
  • canonical shared data links, while concepts, orchestrator-daemon, and embed-daemon must stay local (bootstrapWorktree.mjs:147-169);
  • the repo-local Codex config copied from .codex/config.template.toml;
  • harness-home configuration and profile directories;
  • Codex's own CODEX_HOME/memories and file-backed auth stores;
  • materialized per-agent MCP choices from the Brain-owned sparse configuration contract (#14964).

That creates two bad outcomes: a loud boot failure after process spawn, or a worse silent fallback into ambient/global state. The latter collapses resident identity, auth, memory, or MCP ownership.

The tracked .gemini/settings.template.json is not an Antigravity input. Its history begins as a Gemini CLI workspace configuration, while Google transitioned consumer Gemini CLI to Antigravity CLI on 2026-06-18 (Google announcement). Current Antigravity 2.x uses a global MCP authority; the installed 2.2.1 binary resolves ~/.gemini/config, matching Google's current Antigravity codelab (MCP section). Fleet must target that real contract and must not manufacture .gemini/settings.json.

Live shell-binding evidence

A sterile env -i Zsh login launched from Emmy's exact checkout proved the operator's current path router: ~/.zshenv selected that checkout's ignored .env, shell parsing removed dotenv quotes, and the resulting shell reported identity=neo-gpt-emmy, Emmy's isolated CODEX_HOME, and gh api user --jq .login = neo-gpt-emmy.

That success defines a boundary, not a Fleet dependency. Fleet already resolves the encrypted registry PAT and injects it as GH_TOKEN, and injects NEO_AGENT_IDENTITY, into its minimal child env. It must never parse, copy, or edit operator .zshenv / checkout .env. However, the final checkout path is an input to shell startup: operator-local startup files can source ignored workspace state and shadow the injected values. The cold-bootstrap receipt must therefore exercise the effective shell from the exact final repoPath and fail the resident-ready claim on identity/GitHub mismatch.

The Architectural Reality

  • ensureAgentRepo owns path-safe clone/reuse. A new Fleet composer belongs after it and before FleetLifecycleService.start.
  • hydrateCurrentWorktree is the existing idempotent checkout-hydration primitive. Fleet should import/compose it, not shell out to its CLI and not duplicate its blocklists.
  • .codex/config.template.toml is explicitly copied to an ignored repo-local .codex/config.toml (lines 1-10); it enables hooks but does not own the global CODEX_HOME memory/auth policy.
  • #14964 owns persisted harness choice plus sparse MCP overrides. Bootstrap consumes the Brain-resolved effective configuration; it never invents a second matrix or persists resolved defaults.
  • Antigravity Desktop/CLI use their own global/profile MCP configuration topology. The obsolete Gemini CLI workspace template is a cleanup target, not an onboarding primitive.
  • Secrets remain Brain-side. Workspace/home files may contain executable definitions and non-secret paths, but never PATs, bridge tokens, OAuth tokens, or copied .env contents.

The Fix

  1. Add an idempotent prepareManagedAgentWorkspace Fleet composer (sibling to ensureAgentRepo / startAgentProvisioned) with injected filesystem/hydration seams.
  2. After clone/reuse, hydrate the checkout through the existing import-safe bootstrap primitive. Do not run install/build, and do not clobber an existing overlay.
  3. Materialize only the repo-local adapters that actually exist:
    • Codex: copy .codex/config.template.toml to ignored .codex/config.toml when absent.
    • Claude: retain the existing .claude/settings.template.json.claude/settings.json contract.
    • Antigravity: create no .gemini/settings.json; the legacy Gemini CLI template is not consumed.
  4. Materialize the isolated harness home from the resolved harness/MCP configuration:
    • Codex/Codex Desktop: create the contained home, memories/, and a minimal managed config.toml with file-backed CLI/MCP OAuth stores and features.memories = true.
    • Claude Desktop: probe and materialize the profile-scoped MCP config consumed by the directly spawned --user-data-dir instance.
    • Antigravity: target the installed-version global/profile MCP authority (~/.gemini/config/mcp_config.json for installed 2.2.1). Prove a contained per-resident config-root mechanism; if the product exposes no safe override, refuse with a named unsupported-state result instead of sharing one resident's identity-bound MCP config.
  5. Make re-entry convergence-aware: missing files are created; matching files are no-op; divergent existing operator files refuse with a diff/owned-key reason rather than being overwritten.
  6. Return the canonical prepared repoPath and feed it into the Fleet start path. It remains the child cwd; Codex Desktop additionally consumes the same path as --open-project=<repoPath>.
  7. Treat operator shell startup as an external override boundary, never as configuration substrate: do not read or mutate ~/.zshenv / checkout .env. The L3 cold-bootstrap receipt runs a sterile shell from the exact final repoPath under Fleet's composed child env and proves the effective identity plus GitHub login still match the Fleet definition.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
pre-launch workspace stage startAgentProvisioned clone/reuse → hydrate workspace/home → start any preparation failure prevents spawn composer JSDoc call-order/failure unit matrix
final workspace + effective shell receipt prepared repoPath + lifecycle reserved child env one path drives child cwd and project-opening argv; L3 exercises startup files from that cwd identity/GitHub mismatch is not resident-ready; no dotfile mutation boundary JSDoc sterile-shell exact-cwd L3 receipt
checkout hydration hydrateCurrentWorktree + its blocklists AI overlays, shared data links, read aliases, handoff, Claude settings divergent real paths refuse; no --force existing bootstrap JSDoc real temp-checkout spec
effective MCP config #14964 Brain-side sparse overrides + catalog defaults materialize resolved intent per harness without persisting defaults unknown server/key refuses adapter JSDoc sparse/default evolution specs
Codex home Codex CODEX_HOME config contract independent config/auth/memory root, file stores, memories enabled divergent managed keys refuse generated-file header + JSDoc two-agent no-cross-link probe
Claude Desktop profile exact app-profile probe profile-scoped MCP configuration for the isolated user-data dir unsupported path refuses adapter JSDoc live profile materialization receipt
Antigravity MCP authority installed Antigravity binary + current Google docs contained per-resident global/profile mcp_config.json; no Gemini CLI workspace settings refuse if config-root isolation cannot be proven adapter JSDoc two-profile identity/config negative probe

Decision Record impact

aligned-with ADR 0020 (Fleet owns repo provisioning and spawn-time drift removal), ADR 0034 §2.1/§2.3 (one lifecycle owner, injected data root, secrets never renderer-readable), and ADR 0019 (config values have one declarative owner and are read at use sites). No ADR amendment is required.

Acceptance Criteria

  • startAgentProvisioned executes clone/reuse → workspace/home preparation → supervised start in that order; a preparation rejection proves start was never called.
  • Existing bootstrap primitives are reused without invoking the CLI, dependency install, or build-all.
  • AI config overlays, canonical data links/read aliases, Sandman handoff, and Claude settings converge idempotently while the documented clone-local data dirs remain real/local.
  • Codex project config and an independent CODEX_HOME are materialized; file-backed auth stores and features.memories = true are present; two residents cannot resolve the same memory/auth path.
  • Effective per-agent MCP choices come only from the Brain-owned #14964 contract; secrets and resolved-default persistence remain absent.
  • Claude Desktop's isolated profile receives its proven MCP config location.
  • Antigravity uses its current global/profile mcp_config.json contract with a proven per-resident root; no .gemini/settings.json is created, and an unproven relocation fails before launch.
  • Re-entry reports CREATED | MATCH | DIVERGENT per owned artifact and never overwrites divergent operator content.
  • The prepared repoPath is the single checkout truth passed to lifecycle cwd and any harness-specific project-opening argv; no second workspace path is derived.
  • A sterile-shell L3 receipt from that exact repoPath proves the effective NEO_AGENT_IDENTITY and live GitHub login match the Fleet definition after startup files run; the implementation neither depends on nor edits operator dotfiles.
  • No .env, PAT, bridge token, OAuth token, or credential bytes are copied/rendered/logged.
  • Focused unit/temp-checkout evidence plus one L3 cold bootstrap for Codex Desktop, Claude Desktop, and Antigravity is attached to the PR.

Out of Scope

Repo cloning/path safety (already ensureAgentRepo) · harness process supervision/launch tuples · account creation/OAuth execution · cockpit form design (#14614) · install/build of the cloned repo · arbitrary operator dotfile synchronization · continued support for consumer Gemini CLI.

Avoided Traps

  • Do not shell out to bootstrapWorktree.mjs; its CLI unconditionally builds.
  • Do not copy the operator's .env or auth files into a peer.
  • Do not symlink the .neo-ai-data parent or clone-local PID/concept directories.
  • Do not treat the legacy .gemini/settings.template.json as Antigravity configuration.
  • Do not silently overwrite divergent local configuration.

Related

Parent #13015 · conductor #14937 · launch coverage #14914, #14972, #15047 · persisted configuration #14964 · cockpit consumer #14614.

Origin Session ID: de713f27-0e82-4960-b4c6-f281e0c36449

Retrieval Hint: query_raw_memories("Fleet Manager peer workspace bootstrap .neo-ai-data Codex memories Claude Desktop Antigravity profile")

tobiu referenced in commit ed8ce36 - "feat(fleet): prepare managed workspaces before launch (#15048) (#15129)" on 4:29 PM
tobiu closed this issue on 4:29 PM