Give every session a title
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
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
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
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 arelay: 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
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
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
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 arelay: 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
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:
Next
Handoffs and forks
The other seam: work that spans sessions rather than running beside them.
Sessions reference
Every relay verb and flag.