Broadcasting & Runs
How a Fleet broadcast works: live keystroke mirroring, per-target variable resolution and overrides, supervised runs with durable history, and what happens when a target rejects the write.
Two Broadcast Paths
Once a fleet is armed, everything you type in the primary terminal goes to every armed peer. There are two paths, and they behave differently enough to be worth naming.
- Raw input mirroring replicates keystrokes as you type. It is a live stream with no payload and nothing to replay.
- Structured submission is what Enter does. The composed draft becomes one payload per target, with variables resolved per worktree, and the result is tracked as a supervised run.
Arming is the consent boundary for both. There is no confirmation step on submit — once a fleet is armed, submit just submits. Destructive payloads, large pastes, and multi-line text all fan out immediately. If you want to check what each target will receive before sending, the per-target preview is the review surface.
Live Keystroke Mirroring
When two or more terminals are armed and you start typing in the primary terminal's input bar, an amber Mirroring to N peers pill appears in that input bar. Every keystroke in the primary is replicated live into the input bars of all armed peers. Backspaces, arrow keys, and selections all travel through.
The input bar itself tints amber on the fleet primary, in the same amber family as the pill, so the shell around what you are typing says "Enter broadcasts" at the point of typing rather than only in a chip above it.
Submitting with Enter
Pressing Enter in the focused primary sends the composed draft to every armed peer. The progress
counter on the ribbon shows completed/total while the fan-out is in flight, except on
a small fleet where the broadcast finishes inside the Doherty threshold, in which case the counter
is suppressed to avoid a flicker. A successful, uncancelled fan-out fires a one-shot flash on the
ribbon and a short confirmation sound.
A second Enter while a broadcast is in flight pre-empts the first. The earlier run is recorded as superseded rather than silently dropped.
Broadcast history is per-project. Press ↑ and ↓ in the primary input bar to walk previous broadcasts the same way you walk shell history.
Per-Target Preview and Overrides
The drafting pill becomes clickable when the current draft contains at least one {{variable}}. Click it to open the Fleet broadcast
preview popover. Each row is one armed target, showing an include checkbox, the
terminal's title, the raw draft with amber spans on the variables, and an editable textarea
holding the resolved payload that target will actually receive.
Edit a textarea to set a per-target override. Uncheck the include checkbox to skip that target for this broadcast. The pill grows a divergence dot whenever any override or skip is pending, so you can tell at a glance that the fan-out is no longer uniform. Pressing Esc inside an open textarea reverts that target to the resolved default and blurs the field rather than closing the popover.
Overrides and skips are per-broadcast. Enter snapshots them at press time, so a popover that closes mid-send cannot change what was already captured, and the store is cleared once the send settles. They are also reconciled against the armed set: a pane that leaves the fleet — by manual disarm, or by auto-prune when it is trashed or its PTY dies — drops its override and its skip flag on the way out. Without that, a disarmed-then-rearmed pane would carry a stale skip and quietly miss the next broadcast.
Skipping every target is handled explicitly: the Enter is absorbed, nothing is dispatched, and the announcer says the broadcast was skipped because all targets were excluded.
Variables in Fleet Broadcasts
Fleet broadcasts resolve a small set of variables per target, using each terminal's worktree context. This is the mail-merge path: you write one draft, and every armed terminal substitutes its own values before anything reaches the agent. Names are case-insensitive.
| Variable | Resolves To | Example |
|---|---|---|
{{issue_number}} | Issue number linked to this target's worktree | #412 |
{{pr_number}} | Pull request number linked to this target's worktree | #418 |
{{number}} | Issue number if set, otherwise PR number | #412 |
{{worktree_path}} | Absolute path to this target's worktree directory | /Users/you/proj/.worktrees/feature |
{{branch_name}} | Branch name (falls back to the worktree name if no branch is set) | feature/issue-412 |
A variable that cannot be resolved for a given target — no linked issue, no worktree association — resolves to an empty string, and the preview row flags it. A name Daintree does not recognize is left in the text unchanged rather than blanked, so a typo reaches the agent visibly instead of silently vanishing.
{{project_root}} or {{endpoint}}, that is the remote-compute namespace and it does
not resolve inside a Fleet broadcast.Supervised Runs
A structured broadcast does not end when the bytes land. Daintree tracks it as a run: one submission, followed by a watch phase that follows each target's agent state until every target settles. Settling means leaving working or directing — an agent that comes back waiting for input has settled, because it is done needing the machine and started needing you.
| Status | Meaning |
|---|---|
submitting | The fan-out is still writing to targets. |
watching | Every write has landed; targets are still working. |
completed | Every sent target settled. |
cancelled | You aborted mid-submission. |
failed | No target accepted the write. |
superseded | A newer broadcast pre-empted this run before it settled. |
The ribbon reuses the progress counter's slot for this. While a run is watching it shows live
counts — 3 working · 1 waiting · 2 done — and a finished run
leaves a dismissible Run finished summary. A run that failed outright reads Run failed · nothing sent. Cancelled and superseded runs render nothing,
because the announcer and the progress counter already covered them.
The count chip's armed list is the per-target drill-down for the same run: a pane whose write was rejected carries a Send failed marker beside its live agent-state badge.
Run History
When a run finalizes it appends a record to a durable history kept in the main process, alongside the records that recipe runs write. Because it lives outside the renderer it survives reloads and window eviction. Read it at Settings → Integrations → Run history.
A fleet record carries the run's outcome, a short preview of the draft, target and success and failure counts, and a per-target list with each pane's title and final state. References are stored as snapshots taken at run time and never re-resolved, so a record stays readable after the terminal is closed or the worktree is deleted. The buffer is a ring capped at 100 records, roughly 200 KB on disk. Scrollback and git state are deliberately not recorded; the record is the automation outcome, not a transcript.
Reading a Run from an Agent
fleet.getRunStatus returns the current run in one call over MCP: per-target submission outcome, whether each failure was
permanent or transient, a live agent-state snapshot, settled flags, and aggregate counts. It is
read-only and dispatches nothing.
terminal.sendCommand per terminal and watches with batched terminal.getStatus calls or a bounded wait. The agent state in a run snapshot is a
heuristic and a parsed check result is not a process exit code — confirm both before acting
on them.Large Broadcasts and Cancelling
Daintree batches large broadcasts so the renderer stays responsive. When a payload reaches 100 KB and there are more than five targets, the fan-out runs in batches of five, yielding to the event loop between batches so the main thread can render and drain IPC.
While a batched broadcast is in flight the ribbon shows a Cancel button next to the progress counter. Cancelling stops the remaining batches and dismisses the counter.
Failure Handling
Broadcasts fail for two distinct reasons, and Daintree treats them differently.
Permanent PTY errors (EPIPE, EIO, EBADF, ECONNRESET, ENOTCONN, ENXIO, EINVAL)
auto-disarm the failing target. The PTY is gone or invalid, so retrying would do nothing. No
failure chip is recorded for these, because disarming would auto-dismiss it immediately.
Transient errors leave the armed set intact and surface a failure banner above
the ribbon reading Broadcast failed, naming how many terminals rejected the write
and, when both happened in the same broadcast, how many unreachable terminals were disarmed
alongside them. A Retry failed button re-dispatches the last payload to the
still-failed IDs only, through fleet.retryFailures. Re-entrancy is guarded, so
retries cannot stack.
Raw keystroke broadcasts cannot be replayed — there is no persistent payload for a stream of keystrokes. When the failure was raw input, the banner says so ("Single keystrokes can't be replayed") and omits the retry button entirely rather than showing a dead one.