Skip to main content
The relay is for sessions that are open at the same time. One can hand work to another, ask it a question, or claim a file so nobody collides. It exists so you stop copying context between terminals by hand.

Give every session a title

Why it works. A title is a stable name for a live session, and everything else addresses that name rather than a session id you would have to look up. base relay sessions lists titled sessions in the global registry, which is what task and ping targets resolve against. The gotcha. Do this first, in every session. Until a session has a title it cannot be addressed and cannot receive anything. A session you forgot to register is invisible, not merely hard to reach. A quiet session is listed as idle with its age, never as dead. --project with no relay store of that name registers the title globally only, and says so, with the command that joins the board.

Start the inbox watcher

Messages reach a session on its next prompt. A session that should react while it is idle needs an inbox watcher, and base relay arm prints exactly what to start it with: the fields for Claude Code’s Monitor tool, the step to run when the Monitor expires (every 30 minutes, the host’s limit), and the status line. A session whose title is in use and has no watcher gets one line, once: relay: <title> has no inbox watcher · run base relay arm and start the Monitor it prints. base config set relay.wake_nudge false turns that line off. Since 0.16.0 the shipped watcher no longer drops pings. It used to announce at most 5 waiting pings and mark the rest as read. A session running an older watcher script is told to re-arm.

Handing over real work

Why it works. A task carries a briefing document, so the receiving session has everything it needs to work without asking you. It is shown on the receiving session’s next prompt, and it crosses workspaces through the global tier. The gotcha. The receiver clears it with base relay done refund-handling. Until then base relay tasks keeps listing it, and session start lists unanswered messages in one line, which is the point: a task that stops reminding the receiver is a task that gets dropped. Next rung. Keep the slug and the briefing document’s filename identical. That way the slug shown in base relay tasks is enough to find the document.

Asking a quick question

Why it works. A ping has no document and no completion step. It is shown in the receiver’s next prompt, or at once by its inbox watcher, and their reply is what clears it. What the receiver sees. Since 0.16.0 a message reads as information, never as an order that outranks the person at the keyboard: who sent it, when, and the command to answer it, in one line. It is shown once, never on a tool call. An unanswered one is listed at session start. Reply to clear it:
When to use which. If it needs a document, it is a task. If it fits in three sentences, it is a ping. Sending a task’s worth of context as a ping means that context has nowhere to live afterwards. base relay poll --all --from builder and base relay tasks --from builder show one sender’s messages, including ones already seen.

When a title passes on

A message is addressed to the session that held the title when it was sent, and is delivered to that session only. When a title passes to a new session, for example a fresh session taking over a codename, the earlier holder’s undelivered messages are archived, and each sender gets a relay: not delivered notice naming who holds the title now. Send it again to reach the new holder. A ping that waits more than a day undelivered is archived and its sender told the same way.

Waiting instead of polling

Why it works. wait blocks and costs nothing while it waits. A polling loop spends a tool call and some context on every tick and finds nothing almost every time. Narrow what it wakes for with --from, --type and --project, and bound it with --timeout (default 300 seconds).

Not colliding

Why it works. Claims are advisory. Nothing enforces them, and an expired claim simply stops appearing. What they buy you is that base relay board shows who is where before two sessions open the same file. The gotcha. --force on release takes someone else’s claim. Use it when a session died holding one, not to win an argument.

When a message seems not to arrive

Without an inbox watcher, delivery happens on the receiver’s next prompt. A receiving session sitting idle never sees a waiting message until someone types in it. Start a watcher with base relay arm in any session that should react on its own.
This is the single most common confusion with the relay, and it looks exactly like a message that was never sent. The other cause since 0.16.0: the title passed to a new session after you sent it, and the sender got a relay: not delivered notice. The second gotcha. base relay poll and base relay board are scoped to the workspace, while tasks and pings route through the global session registry. So “No relay stores exist” from board is not evidence that a message did not arrive. base relay sessions showing the target as live is the more trustworthy signal.

The lower-level primitive

--type takes claim, release, notify, unblock, contract-change, ready-to-merge, question or answer. --to takes a title, a session id, phase:<n>, or all. The gotcha. base relay poll consumes the messages it reads. Use --peek when you want to look without clearing.

Tearing it down

The store is deliberately disposable: it is a working surface for a milestone, not a permanent record. export writes a read-only snapshot first if you want to keep any of it, and dispose previews until you pass --force.

Wrapping it in star commands

Everything above is the command line. If you would rather type *task builder implement partial refunds than the full command, that is a star command you write yourself:
base does not ship these. The relay commands are the product; the shorthand is yours.

Next

Handoffs and forks

The other seam: work that spans sessions rather than running beside them.

Sessions reference

Every relay verb and flag.