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 (
define → repo → roster): 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 (
preflight → launch → auth): 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=1 → 57 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
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).
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.define→repo→roster): reads and writes the Fleet registry through the one long-lived HTTP owner, refuses occupied-definition drift, creates or reconciles exactmetadata.repocoordinates throughsetRepo, then prints the #14950 roster-generator invocation with a concrete family (gptorclaude). Clone credentials and terminal-control payloads are rejected before persistence, and dry-run output never prints the clone URL.origin/dev, and restart the Memory Core server. The conductor verifies membership fromgit show origin/dev:ai/graph/identityRoots.mjs, not from its current feature branch, so an unmerged local roster entry cannot unlock Phase B.preflight→launch→auth): both the merged roster entry and seeded graph node must be verifiable. Missing or unreachable graph state isREFUSE, 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.instanceHomeand actual executable path. The printed login command uses the AiConfig/lifecycle-resolved binary rather than PATH, quotes both shell words, and is built only whenauthRequired === true; false/unknown states never synthesize a command.The planner remains pure: the CLI gathers observed facts, while dry-run and
--commitconsume 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
npm run ai:fleet-server) instead of constructing process-local service singletons. This is required for cross-shell lifecycle truth.origin/devartifact rather than the current worktree.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=1→ 57 passed, twice on the final staged delta.origin/devroster lookup, fail-closed graph/repo/definition cases, clone-secret/output controls, actual/bin/shlogin-line execution, lifecycle auth/home/binary status, and repo-path containment.node --checkpassed for all four modified modules/specs.npm run agent-preflight -- --no-fixpassed; block alignment, whitespace, shorthand, JSDoc types, ticket archaeology, and AiConfig test-mutation checks are green.Post-Merge Validation
npm run ai:fleet-server, then onboard the second GPT-family peer end-to-end: Phase A--commit, roster PR via #14950, merge + refreshorigin/dev+ Memory Core restart, Phase B--commit, one login using the printed resolved-binary command, then first boot self-registers its wake route.OK, and that stopping the graph produces a namedREFUSEwithout 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).