Root cause of a spawned agent's failed first send: fleet agents are born not
knowing how to reach their peers. Inject a comms cheat-sheet + peer roster into
each fleet agent's system prompt via composeContract (the runtime-agnostic path
every `mosaic yolo <runtime>` agent hits), so it can reach the orchestrator and
peers from its first turn.
- src/fleet/comms-onboarding.ts (standalone, no fleet.ts coupling):
parseRosterAgents (name/class/host/ssh/socket), renderPeerReach (data-driven
agent-send command), buildFleetCommsBlock (own [host:session] identity +
agent-send path + peer table + FLIP-to-reply + `agent send --verify`=ACCEPTED),
readFleetCommsBlock (reads roster.yaml; '' when not a member).
- launch.ts composeContract appends it only when MOSAIC_AGENT_NAME is set + the
agent is in the roster (no-op for non-fleet launches).
- roster.schema.json: optional per-agent host/ssh/socket (cross-host + socket
addressing; manual cross-host listing is a pre-federation stopgap).
Address rendering is fully data-driven per Mos's acceptance criteria:
- cross-host: local → `-s <session>` (no -H); remote → `-H <ssh> -s <session>`.
- socket: named → `-L <socket>`; unset → default socket (no -L) — matches the
LIVE socket, never blindly inherits the roster's tmux.socket_name.
Verified: 14 onboarding + 9 composeContract tests (parse, render local/remote/
fallback/equal-host/socket-none/named/combined, build, situational read +
composeContract injection w/ correct cross-host+socket addrs, no-op when
MOSAIC_AGENT_NAME unset). tsc/eslint/prettier/sanitize clean.
Refs #620
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EsgTQzV5YUGk1JtCLP4B83