The first turn after an ADR-0017 FE relaunch, and the manual bootstrap for any
frontend-side session. The resumed claude --continue is reactive and deaf:
harness Monitors do NOT survive a relaunch (the session ended; --continue
restores the transcript, not live background tasks), so nothing wakes the
session on inbound fast-comm until a Monitor is re-armed on a turn. This skill
is that turn — keep its steps current as the setup evolves (that's the point of
it being a skill rather than a hardcoded resume command).
A user roams across SEVERAL Windows FEs against ONE backend. Two facts shape comm:
-
Set the FE handle — must mirror the Rust frontend's self_comm_handle():
win-fe-<lowercase HOSTNAME or COMPUTERNAME>.
PowerShell:
$hostName = if ($env:HOSTNAME) { $env:HOSTNAME } elseif ($env:COMPUTERNAME) { $env:COMPUTERNAME } else { "unknown" }
$env:SOT_COMM_NAME = "win-fe-$($hostName.ToLowerInvariant())"
Git Bash / bash:
export SOT_COMM_NAME="win-fe-$( (hostname -s 2>/dev/null || hostname || printf unknown) | tr '[:upper:]' '[:lower:]' )"
If ~/.sot-comm/bin is installed on this machine, join locally so
comm-relay.sh sends with the right from: name:
~/.sot-comm/bin/comm-join.sh --name "$SOT_COMM_NAME" --expertise "Ship of Tools frontend terminal"
This local join is for the FE machine's tools. Do not expect it to create a
row in the backend host's shared-home registry. If sot-comm isn't installed
at all, install first (julia --project=. -e 'using ShipTools; ShipTools.update_comm()'),
then join — the BE ping below needs the joined handle so agent.send stamps
from:win-fe-<host>.
-
Point sends at the local tunnel — socket-only backend mode means the
remote backend normally listens on a private Unix socket, discovered on the
backend host with sotd session-socket-path ${SOT_BACKEND_LABEL:-sot}. The
frontend launcher opens a local TCP port that forwards to that remote
Unix socket. Set relay sends to the local tunnel:
PowerShell:
$port = if ($env:SOT_PORT) { $env:SOT_PORT } else { "18743" }
$env:SOT_RELAY_ENDPOINT = "tcp:127.0.0.1:$port"
Git Bash / bash:
export SOT_RELAY_ENDPOINT="tcp:127.0.0.1:${SOT_PORT:-18743}"
Do not try to connect to 127.0.0.1:18743 on the remote backend. In
socket-only mode that port belongs only on the frontend machine, and only
while the SSH tunnel/launcher is running. Browser helper ports 1234-1240
must also be forwarded by the launcher for Pluto, docs, and static pages.
-
Re-arm the fast-comm wake — arm a persistent harness Monitor on the
FE inbox:
- Windows:
%LOCALAPPDATA%\sot\fe-inbox.jsonl
- Linux/macOS frontend:
${XDG_STATE_HOME:-$HOME/.local/state}/sot/fe-inbox.jsonl
The filter must (i) drop this session's OWN echoes only, and (ii) wake on
lines addressed to my handle or the win-fe family label — NOT lines
aimed at another handle (a sibling FE, or backend-dev), and NOT
broadcasts (to:""). Broadcasts are demoted: they FILE in the inbox but
don't wake (noise reduction — important broadcasts go on the durable bus,
read on /bus-sync):
HANDLE="win-fe-$(hostname | tr 'A-Z' 'a-z')"
state="${XDG_STATE_HOME:-$HOME/.local/state}/sot"
[ -n "${LOCALAPPDATA:-}" ] && state="$LOCALAPPDATA/sot"
mkdir -p "$state"; touch "$state/fe-inbox.jsonl"
tail -n0 -F "$state/fe-inbox.jsonl" \
| grep --line-buffered -v "\"from\":\"$HANDLE\"" \
| grep --line-buffered -E "\"to\":\"($HANDLE|win-fe)\""
(The trailing " in the -E pattern anchors each alternative, so win-fe
matches the bare family label but NOT win-fe-<otherhost>; no empty
alternative means broadcast to:"" files without waking.) Use the Monitor
tool with persistent: true. A plain background tail does NOT wake you —
only a Monitor turns each new inbox line into an event. Local frontend
storage is not NFS, so tail -F is appropriate here. Without this Monitor,
the FE receives messages but this Claude session will not wake on them.
-
Ping the BE — prove the daemon is dispatching AND the FE is writing
the inbox (the exact substrate the Monitor depends on), not merely that the
FE process is up. There is no dedicated ping op; instead round-trip a
self-addressed agent.send and confirm the nonce lands back in
fe-inbox.jsonl. The path it exercises is the whole wake chain:
relay → daemon socket → broadcast → FE → inbox write.
HANDLE="win-fe-$(hostname | tr 'A-Z' 'a-z')"
NONCE="be-ping-$$-$RANDOM"
INBOX="$state/fe-inbox.jsonl"
~/.sot-comm/bin/comm-relay.sh send "@$HANDLE" "BE liveness ping $NONCE"
for i in $(seq 1 10); do
grep -q "$NONCE" "$INBOX" && { echo "BE OK — round-trip:"; grep "$NONCE" "$INBOX"; break; }
sleep 0.5
done
Read the result:
relayed -> win-fe-<host> via tcp:... = the daemon socket is alive and acked the send.
- Nonce reappears in
fe-inbox.jsonl (with a daemon-stamped ts) = daemon
broadcast it and the FE wrote it → entire wake path is live.
- Ack but no inbox line = daemon alive but the FE isn't writing the inbox
(wake is broken — investigate the FE↔daemon connection / relaunch the FE).
- No
relayed line = daemon or the SSH-forwarded tunnel is down.
Self-addressed (@$HANDLE) on purpose: it drops a to:<myhandle> message (no
cross-machine noise) and the Monitor's from:<myhandle> filter means the ping
won't spuriously self-wake you — so verify by reading the inbox file directly,
not by waiting for a Monitor event. On Windows the relay needs
SOT_RELAY_ENDPOINT set explicitly (git-bash has no sotd process to
pgrep); use the same endpoint the FE connects to.
If you know a backend session handle, also send a directed announce
(comm-relay.sh send @<be-handle> "[$HANDLE] FE receive path armed").
Broadcasts may file silently on backend sessions, so they are not a
round-trip proof.
-
Catch the deaf-window gap — read the tail of fe-inbox.jsonl (lines
where from != your handle) and surface anything new. The daemon replays
missed agent.messages into the inbox on FE reconnect, so messages sent
while you were down are usually still there — you only missed the live wake.
-
/bus-sync — the durable git-bus fallback for anything the relay didn't
replay.