LearnNewsExamplesServices
Frontmatter
titlefeat(fleet): two-phase peer-onboarding conductor over owned contracts (#14937)
authorneo-fable
stateOpen
createdAt3:22 PM
updatedAt4:30 PM
closedAt
mergedAt
branchesdevagent/14937-onboarding-conductor
urlhttps://github.com/neomjs/neo/pull/14965
contentTrust
projected
quarantined0
signals[]
Open
neo-fable
neo-fable commented on 3:22 PM

Resolves #14937

The peer-onboarding rail's capstone: ai/scripts/fleet/onboardPeer.mjs — one dry-run-first command walking an operator from "I want a new resident" to "a supervised harness runs in its own home, awaiting exactly one login," as a thin two-phase conductor over owned contracts.

  • Phase A (definereporoster): reads and writes the Fleet registry through the one long-lived HTTP owner, refuses occupied-definition drift, creates or reconciles exact metadata.repo coordinates through setRepo, then prints the #14950 roster-generator invocation with a concrete family (gpt or claude). Clone credentials and terminal-control payloads are rejected before persistence, and dry-run output never prints the clone URL.
  • The operator gate: merge the roster PR, refresh origin/dev, and restart the Memory Core server. The conductor verifies membership from git show origin/dev:ai/graph/identityRoots.mjs, not from its current feature branch, so an unmerged local roster entry cannot unlock Phase B.
  • Phase B (preflightlaunchauth): both the merged roster entry and seeded graph node must be verifiable. Missing or unreachable graph state is REFUSE, never WARN-and-launch. Launch goes through the long-lived Fleet owner, so separately invoked CLI processes share one lifecycle/process map and repeat starts remain idempotent.
  • Auth handoff: lifecycle status returns the non-secret resolved instanceHome and actual executable path. The printed login command uses the AiConfig/lifecycle-resolved binary rather than PATH, quotes both shell words, and is built only when authRequired === true; false/unknown states never synthesize a command.

The planner remains pure: the CLI gathers observed facts, while dry-run and --commit consume the same plan. Engine/model and Social Name inputs deliberately have no surface here; those facts remain observation-owned and ritual-owned respectively.

Evidence: L2 achieved — the integration falsifier starts the real default Fleet HTTP dispatch chain with real registry, manager, lifecycle, and control-bridge collaborators; two independent Node processes define/provision/start and then re-observe/re-start the same agent while the owner records exactly one spawn. Security falsifiers execute the rendered login command through /bin/sh, reject credential-bearing/control-character clone inputs, and assert no injected command runs.

Deltas from ticket

  • The conductor now requires the dev-only loopback Fleet server (npm run ai:fleet-server) instead of constructing process-local service singletons. This is required for cross-shell lifecycle truth.
  • Phase-B roster membership is proven from the merged origin/dev artifact rather than the current worktree.
  • Unreachable graph state fails closed. The original WARN path contradicted the ticket's “refuses until verifiable” acceptance criterion and was removed.
  • A new resident must supply repo coordinates; an existing exact repo may be reused. Launch never falls back to the Fleet owner's cwd.
  • Lifecycle status exposes only two additional non-secret operator-handoff fields (instanceHome, launchCommand) over the already dev-only unauthenticated loopback bridge; no auth contents cross it.

Test Evidence

  • npm run test-unit -- test/playwright/unit/ai/scripts/fleet/onboardPeer.spec.mjs test/playwright/unit/ai/FleetLifecycleService.spec.mjs test/playwright/unit/ai/services/fleet/deriveAgentRepoPath.spec.mjs --workers=157 passed, twice on the final staged delta.
  • The 57-test matrix includes the real ephemeral Fleet HTTP chain with two external Node clients, exact origin/dev roster lookup, fail-closed graph/repo/definition cases, clone-secret/output controls, actual /bin/sh login-line execution, lifecycle auth/home/binary status, and repo-path containment.
  • node --check passed for all four modified modules/specs.
  • npm run agent-preflight -- --no-fix passed; block alignment, whitespace, shorthand, JSDoc types, ticket archaeology, and AiConfig test-mutation checks are green.

Post-Merge Validation

  • Start the canonical loopback owner with npm run ai:fleet-server, then onboard the second GPT-family peer end-to-end: Phase A --commit, roster PR via #14950, merge + refresh origin/dev + Memory Core restart, Phase B --commit, one login using the printed resolved-binary command, then first boot self-registers its wake route.
  • Confirm the canonical live graph produces preflight OK, and that stopping the graph produces a named REFUSE without a spawn.

Commits

  • 2800be120 — initial conductor + pure planner coverage.
  • 1f690632b — exact-head convergence: long-lived owner, fail-closed gates, authoritative roster/auth paths, and integration/security falsifiers.

Related: parent #13015 · consumed contracts: #14918 (launch, merged) · #14950 (roster generator, merged) · closed-by-composition: #14915 · UI twin: #14807 · v13.2 cornerstone-1 (#14560 / #13448).

Authored by Mnemosyne (Claude Fable 5, Claude Code). Session b956ba53-01ed-4ea6-a1e5-62969f887bc3. Exact-head convergence by Euclid (GPT-5.6 Sol, Codex).