Multi-agent sharing
Because journal reads take no lock, any number of agents can share a terminal as an information
source. Concurrent run calls are serialized by a per-terminal writer lock. send, note, and a
human typing through tmux deliberately bypass that lock, so they require their own coordination rules.
Reads are lock-free
peek, log, ls, and wait never take the writer lock and never record journal events. Run them
from as many agents as you like against the same terminal.
First, Agent A creates the shared terminal:
pairmux --json new --name dev
Because wait --pattern observes only output written after the wait begins, Agent B arms the waiter
before the readiness line can be emitted:
# Agent B: this call blocks while Agent A continues independently.
pairmux --json wait dev --pattern "compiled successfully" --timeout 30s
While B is waiting, Agent A starts a reproducible long-lived demo:
pairmux --json run dev 'sleep 2; printf "compiled %s\n" successfully; while :; do sleep 60; done' --timeout 500ms
The emitted output is compiled successfully, but that exact phrase is absent from the echoed
command line, so the waiter cannot report a false readiness match.
Agent C can inspect the same journal without contending with either call:
pairmux --json log dev --grep "error|warn"
pairmux --json peek dev
If the process was already running before an observer arrived, use log --grep to search history
before arming a future-only pattern wait. To close the small gap between a history read and a later
wait, start the waiter concurrently before the action that emits the line, or search history again
after a wait timeout.
run takes a per-terminal lock
run takes a per-terminal writer flock. If another agent concurrently invokes
pairmux --json run dev "echo second" while the lock is held, pairmux returns an E_BUSY envelope
immediately — it does not queue — naming the holder's pid:
{"schema":"pairmux.v1","ok":false,"status":"error","next":["pairmux peek dev"],"error":{"code":"E_BUSY","message":"another writer holds the lock: journal: write.lock held by pid 22018: journal: write lock held by another process","hint":"pairmux peek dev"}}
A related E_BUSY appears if you try to run a second command while a prior one is still running:
error E_BUSY
a command is still running
next:
pairmux peek dev
Because it returns instead of blocking, the caller should use pairmux wait NAME or peek to observe
the current work, then retry only after the returned status allows it. Do not sleep-and-guess. Reading
with peek/log, or using a different terminal, remains available. ls shows the lock holder inline
in the CMD column:
send deliberately does not take the writer lock: its purpose is to answer the pending interactive
program left by run. Send each answer once, then wait and peek rather than repeating input.
note also bypasses the writer lock and appends a side-channel event. Neither command authorizes a
second agent to interfere with arbitrary terminal input.
ok
NAME STATUS MODE CMD
dev running hooks sleep 8 [lock:22018] (2026-07-20T09:14:03Z)
A division-of-labor recipe
Create one terminal for each role that executes commands; a read-only observer does not need its own terminal:
# Agent A
pairmux --json new --name api
# Agent B
pairmux --json new --name tests
Agent C arms readiness observation on api, then Agent A starts the service concurrently:
# Agent C (start this first)
pairmux --json wait api --pattern "service-ready-42" --timeout 30s
# Agent A, while C is blocked
pairmux --json run api 'sleep 2; printf "service-ready-%s\n" "$((40+2))"; while :; do sleep 60; done' --timeout 500ms
Agent B can run its independent work without touching api:
pairmux --json run tests 'printf "tests passed\n"'
Rules of thumb
- Separate command runners into separate terminals. The writer lock arbitrates
run, not every form of terminal input. - Treat
E_BUSYas routine. It is a normal envelope, not a crash — read the holder and choose to wait or read-only. - Reads avoid writer contention. Favor
peek/log/waitfor observation; they take no writer lock and append no journal events. - Coordinate
send,note, and humans separately. They bypass the writer lock. A human leaves a journalednotewhen handing control back; attach itself is not an event. See human collaboration.
Clean up the persistent demo terminals when finished; their journals remain available:
pairmux --json kill api
pairmux --json kill dev
pairmux --json kill tests