# Fleet Launch Runbook

How every Mosaic fleet agent — workers **and** the orchestrator — is launched, and how to
configure each one. The guiding principle: **one roster-driven launcher**. There is no bespoke
per-agent launch script; the roster plus per-agent `.env` files are the single source of launch
config.

## The launch chain

| Layer            | File                                              | Responsibility                                                                                                                                              |
| ---------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| systemd unit     | `mosaic-agent@<role>.service`                     | One templated unit per role; `ExecStart` runs the session launcher with the instance name `%i`. Defaults `MOSAIC_AGENT_RUNTIME=pi`, `MOSAIC_AGENT_NAME=%i`. |
| session launcher | `tools/fleet/start-agent-session.sh <role>`       | Builds the launch command, opens the tmux pane, wires the heartbeat.                                                                                        |
| launch command   | `mosaic yolo <runtime>` (or a per-agent override) | Replaces the pane's foreground process with the runtime, fully seeded.                                                                                      |
| seeding          | `mosaic`'s `composeContract()`                    | Injects the Constitution/USER/TOOLS/runtime contract, `*.local` overlays, **and** the Fleet-Comms cheat-sheet — all via `--append-system-prompt`.           |

Per-agent overrides live in `fleet/agents/<role>.env`, generated from `roster.yaml` by
`generateAgentEnv` (`packages/mosaic/src/commands/fleet.ts`) and consumed by the launcher.

## Worker launch path (default)

1. `roster.yaml` carries each agent's `runtime` and optional `model_hint`.
2. `generateAgentEnv` emits `fleet/agents/<role>.env` with `MOSAIC_AGENT_NAME`,
   `MOSAIC_AGENT_RUNTIME`, and `MOSAIC_AGENT_MODEL`.
3. `start-agent-session.sh` has no `MOSAIC_AGENT_COMMAND` set, so it falls through to the default
   (line ~44):
   ```sh
   MOSAIC_AGENT_COMMAND="mosaic yolo $MOSAIC_AGENT_RUNTIME${MOSAIC_AGENT_MODEL:+ --model $MOSAIC_AGENT_MODEL}"
   ```
4. The launcher bakes `MOSAIC_AGENT_NAME` into the pane command (line ~118), so `composeContract`
   can inject the Fleet-Comms cheat-sheet for that role.

That is the whole worker path: roster → `.env` → `mosaic yolo <runtime>` → seeded pane.

## Orchestrator fold (PATH A — ships today)

The orchestrator is **just another roster agent** launched through the canonical path — not a
snowflake script.

| Piece              | Value                               |
| ------------------ | ----------------------------------- |
| host-side launcher | `orchestrator-launch.sh`            |
| systemd unit       | `mosaic-fleet-orchestrator.service` |
| tmux session       | `orchestrator` (role-named)         |

Set its launch command via `fleet/agents/orchestrator.env`:

```sh
MOSAIC_AGENT_COMMAND='mosaic yolo claude --channels plugin:discord@<channel>'
```

When `MOSAIC_AGENT_COMMAND` is set, `start-agent-session.sh`'s `if [ -z "$MOSAIC_AGENT_COMMAND" ]`
guard (line ~41) is false, so the line-44 default — **including its hardcoded `yolo`** — is skipped
entirely. The override fully controls the runtime and flags. Routing through `mosaic yolo claude`
(rather than a raw `claude` invocation) is what gives the orchestrator the same full
`composeContract` seeding + Fleet-Comms cheat-sheet as every worker, with `--channels` and any
other flags passed straight through to the `claude` binary.

## Launch gotchas

1. **Flag conflict.** `mosaic yolo claude` already injects `--dangerously-skip-permissions`. Do
   **not** also pass `--permission-mode bypassPermissions` — the `claude` binary would receive both.
   Use `mosaic yolo claude …` alone (yolo covers the unattended posture), **or** non-yolo
   `mosaic claude --permission-mode bypassPermissions …`. Never mix the two.
2. **`MOSAIC_AGENT_NAME` must reach the pane.** The launcher bakes it from the instance name, and
   `composeContract` gates the Fleet-Comms block on it (`launch.ts`, in `composeContract`) — **and**
   the role must be a member of `roster.yaml`, or the block resolves empty.
3. **`launchRuntime` guards.** `mosaic yolo claude` runs `checkSoul` / `checkRuntime` /
   `checkSequentialThinking`. The host needs `SOUL.md` and the sequential-thinking MCP, or the
   launch aborts (a raw `claude` invocation skipped these checks). Dry-run the composed command in a
   throwaway tmux session before swapping a live launcher.

## Why per-agent `.env` survives upgrades (#632)

`install.sh` `PRESERVE_PATHS` includes `fleet/*.yaml`, `fleet/agents`, and `fleet/run`, so
`mosaic update`'s framework re-seed **preserves** your roster and per-agent `.env` overrides
(glob-aware `cp` fallback; matching TS parity in `file-adapter.ts`). Before #632, an auto re-seed
could wipe them — which is exactly why PATH A's `.env` override is safe to rely on now.

## Inspecting the comms wiring

- `mosaic fleet comms-block <role>` prints the Fleet-Comms cheat-sheet a given role receives at
  launch — its `[host:session]` identity, the exact `agent-send.sh` command for each peer, and the
  FLIP / `--verify` conventions. `--host <h>` previews a cross-host view. An unknown role or missing
  roster **fails loud** (stderr + non-zero exit), so a typo is never a silent no-op.
- Versus `mosaic compose-contract <runtime>`: that emits the **whole** system prompt and reads the
  role from `MOSAIC_AGENT_NAME` (a full-prompt smoke test). `comms-block` is the targeted,
  explicit-arg, comms-only view — e.g. `mosaic fleet comms-block coder0-0` to preview a peer.

## North Star / future direction

**Vision:** a webUI lets the user edit each agent's launch config — switch **harness**
(claude / pi / codex / opencode), toggle **yolo**, pick a **model**, set a **command/channels**
override — with no terminal.

**Continuity — this is not a new launch path.** It is a data-model + UI-binding layer over the
existing roster-driven launcher. Field-by-field status today:

| Launch-config field      | Roster-native today? | Mechanism / gap                                                                                                                                          |
| ------------------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **harness** (`runtime`)  | ✅ end-to-end        | `roster.runtime` → `generateAgentEnv` emits `MOSAIC_AGENT_RUNTIME` → launcher line 44. UI just writes the field.                                         |
| **model** (`model_hint`) | ✅ end-to-end        | `roster.model_hint` → `MOSAIC_AGENT_MODEL` → launcher line 44 `--model`. UI just writes the field.                                                       |
| **yolo**                 | ❌ new               | Launcher line 44 **hardcodes** `mosaic yolo`. A non-yolo toggle needs a roster `yolo` field → emit `MOSAIC_AGENT_YOLO` → make line 44 conditional.       |
| **command / channels**   | ❌ new               | `MOSAIC_AGENT_COMMAND` is **consumed** (launcher line ~12) but `generateAgentEnv` does not emit it. Needs a roster `command`/`channels` field → emitted. |

**The arc:**

- **A** — `.env` `MOSAIC_AGENT_COMMAND` hatch: manual, ships now, kept safe across upgrades by #632.
- **B** — roster-native launch-config: harness + model are already there; add the **yolo** toggle
  (line-44 conditional) and **command/channels** emission to complete the data model.
- **webUI** — binds dropdowns/toggles directly to those four roster fields.

PATH A's `.env` override is the **manual form** of exactly what PATH B makes roster-native and the
webUI edits — one continuous arc, not three separate features. PATH B is tracked as #636.
