# Fleet Comms Guide

How one seat reaches another on a host. The mechanism is the framework's; the sessions and
sockets are per-host, so measure yours rather than trusting an example.

`mosaic <runtime>` would normally inject the addressing block from the roster. Where the composer
is unavailable, or where the roster is stale, this guide is the substitute.

## Measure the fleet; do not trust the roster

`fleet/roster.yaml` is a declaration of intent, not an observation. It routinely names a socket
that was never created, lists seats that are not running, and omits seats that are — this was
all three have been observed true at once on a live host. Find out what is actually
up before addressing anyone:

```bash
tmux list-sessions
tmux list-panes -a -F '#{session_name}  #{pane_current_command}  #{pane_current_path}'
```

The pane command tells you the runtime. A pane showing `bash` is an idle shell with no agent
attached — a send there lands in a shell prompt and is not read by anyone.

Use the **default socket**. Do not pass `-L mosaic-fleet` on the strength of the roster.

## Sending

```bash
~/.config/mosaic/tools/tmux/agent-send.sh -s <dst_session> -C <class> -m "<message>"
```

`-s` also accepts `session:window.pane`. `-f <file>` sends a file body; stdin works too.

### Classes

`-C` takes exactly one of these. Anything else exits 3.

| Class          | Use for                                                  |
| -------------- | -------------------------------------------------------- |
| `terminal-log` | log only; never needs the agent's attention              |
| `actionable`   | a decision, blocker, gate, or question needing an answer |
| `human`        | relayed from a human operator                            |
| `reaction`     | an ack or acknowledgement token                          |
| `digest`       | machine wake, coalescible                                |

An absent class is treated as `actionable` by consumers, which is the fail-safe direction. Prefer
naming it anyway.

### Addressing preamble

The wire format is `[<src> -> <dst> class=<class>] <body>`. Flip it when you reply — the tool
sends, it does not auto-reply.

### Exit codes

| rc  | Meaning                                        |
| --- | ---------------------------------------------- |
| 0   | delivered or queued                            |
| 1   | target session not found                       |
| 2   | text reached the pane but is **still a draft** |
| 3   | usage error (bad class, missing `-s`)          |

**Never retry on rc=2.** The message is in the target pane; retrying double-sends it. Confirm
instead:

```bash
tmux capture-pane -p -t <session>:0.0 | tail -20
```

rc=2 is the normal result when the target is an idle pi seat.

## Durable comms

tmux delivery is host-local and does not survive a pane. Anything that must outlive the session
goes through the estate's durable comms protocol — a committed `comms/` tree in an estate repo,
with its own README. Use it for cross-host messages, verdicts, and anything a later session needs
to find.

## Handing work across seats

1. **A verdict handed to the requesting seat is a legitimate delivery path**, and the required one
   for anything `pr-review.sh` would otherwise post (see `guides/CODE-REVIEW.md`).
2. **Address the seat, not the runtime.** A seat name is a session name; whether it runs claude,
   pi or codex is not the sender's business.
3. **Say what you measured, not just what you concluded** — the receiving seat cannot see your
   terminal.
