aboutsummaryrefslogtreecommitdiff
path: root/claude-templates
diff options
context:
space:
mode:
Diffstat (limited to 'claude-templates')
-rw-r--r--claude-templates/.ai/protocols.org10
-rwxr-xr-xclaude-templates/bin/agent-text62
2 files changed, 48 insertions, 24 deletions
diff --git a/claude-templates/.ai/protocols.org b/claude-templates/.ai/protocols.org
index 3e32074..bf0e6f7 100644
--- a/claude-templates/.ai/protocols.org
+++ b/claude-templates/.ai/protocols.org
@@ -469,13 +469,17 @@ Two channels, two trigger words. "page me" is the desktop, "text me" is the phon
agent-text "Message for Craig's phone"
#+end_src
- =agent-text= (in =~/.local/bin= via the rulesets install) sends from the dedicated Signal identity (+15045173983) to Craig's Signal account UUID, firing a normal mobile push. The account is registered on velox (primary) and ratio (linked device), so either sends directly; a machine without it ssh-relays to velox. Verified end to end 2026-07-13 (velox) and 2026-07-20 (ratio). Never target Craig's phone *number* (it reads as unregistered in Signal's directory); the script targets the UUID.
+ =agent-text= (in =~/.local/bin= via the rulesets install) sends from the dedicated Signal identity (+15045173983) to Craig's Signal account UUID, firing a normal mobile push. Never target Craig's phone *number* (it reads as unregistered in Signal's directory); the script targets the UUID.
- Caveats: a relay from a non-linked machine needs velox up on the tailnet, and each device holding the account wants a periodic =receive= (the signal-receive timer handles that). The full runbook lives in rulesets =docs/design/=.
+ *Which machine holds the account is a runtime fact, not a fixed one — derive it, don't assume it.* Any machine whose local =signal-cli= holds the identity sends directly, whether as the registered primary or as a linked device; a machine without it ssh-relays to the first relay host that does. Check the local half with =signal-cli listAccounts=. I write it this way because the old wording named velox as the permanent primary, and velox's 2026-08-13 reinstall wiped that registration — which broke the doc and the script together, since =agent-text= had the same host hardcoded as its only relay target.
+
+ The relay list lives in =AGENT_TEXT_RELAYS= at the top of the script, tried in order, and is overridable for a one-off (=AGENT_TEXT_RELAYS="host1 host2" agent-text "..."=). It skips any candidate whose short name matches =uname -n=: reaching the relay branch means the account is *not* local, so an ssh round trip to this machine lands on the same empty store and cannot succeed.
+
+ Caveats: a relay needs at least one listed host up on the tailnet *and* holding the account, and each device holding it wants a periodic =receive= (the signal-receive timer handles that). A relay to a host that is reachable but unregistered fails cleanly rather than silently — =signal-cli send= exits 1 with "User <number> is not registered", so the loop moves on to the next candidate (verified 2026-08-19). The full runbook lives in rulesets =docs/design/=.
- *"text and page me" — both.* Fire =agent-text= and =notify= together. The phone reaches him now, the desktop note waits for his return. This is the default when a run can't tell whether he's away.
-On velox, Claude sessions may also have the *signal-mcp* tool (=send_message_to_user=, same identity), fine to use there, but it exists only in velox's local MCP config, so =agent-text= is the portable habit. The tool was named =agent-page= before 2026-07-20; a deprecated =agent-page= shim still delegates to =agent-text=. Do *not* use the old =page-signal= shell script (removed 2026-06-12).
+A *signal-mcp* tool (=send_message_to_user=, same identity) has existed in one machine's local MCP config before now. Treat it as a nicety that may or may not be registered on the machine you're on — it was gone from velox after the 2026-08-13 reinstall — and use =agent-text= as the habit, since that one travels with the rulesets install. The tool was named =agent-page= before 2026-07-20; a deprecated =agent-page= shim still delegates to =agent-text=. Do *not* use the old =page-signal= shell script (removed 2026-06-12).
* Session Protocols
diff --git a/claude-templates/bin/agent-text b/claude-templates/bin/agent-text
index 86aa933..2eccb2e 100755
--- a/claude-templates/bin/agent-text
+++ b/claude-templates/bin/agent-text
@@ -5,31 +5,38 @@
#
# Usage: agent-text <message...>
#
-# The Signal identity (+15045173983) is registered in velox's signal-cli, and
-# any daily driver linked as a device of that account (ratio, 2026-07-20) can
-# send directly too. So the dispatch is: if the account is registered in the
-# local signal-cli, send directly; otherwise ssh-relay the send to velox over
-# the tailnet. A direct send from a linked device still lands when velox is
-# down (the reason ratio was linked). The recipient is Craig's Signal account
-# UUID; his phone number reads as unregistered in Signal's directory, so never
-# target the number. Verified end to end 2026-07-13 (velox) and 2026-07-20
-# (ratio, direct).
+# The Signal identity (+15045173983) is held by any daily driver that has it in
+# its local signal-cli, whether as the registered primary or as a linked device
+# (ratio, 2026-07-20). So the dispatch is: if the account is registered locally,
+# send directly; otherwise ssh-relay the send to the first relay host that
+# actually holds it — a reachable but unregistered host fails and the loop
+# advances. The recipient is Craig's Signal account UUID; his phone
+# number reads as unregistered in Signal's directory, so never target the
+# number. Verified end to end 2026-07-13 (velox) and 2026-07-20 (ratio,
+# direct). The relay path was verified velox -> ratio on 2026-08-16 by
+# archsetup, who reported the exact relay command returning a Signal
+# timestamp with rc 0; that one is on report, not re-run here.
#
# This is the AWAY channel. At his desk, use the desktop channel instead:
# notify info "Title" "Message" --persist
# See protocols.org "Reaching Craig" for choosing between them.
#
-# Known caveats (full runbook in rulesets docs/design/): a relay from a
-# non-linked machine needs velox up on the tailnet, and each device holding the
-# account wants a periodic `receive` (staleness warnings appear otherwise); the
-# signal-receive timer handles that.
+# Known caveats (full runbook in rulesets docs/design/): a relay needs at least
+# one listed host that is both up on the tailnet AND holding the account -- a
+# reachable host without it fails and the loop moves on. Each device holding the
+# account wants a
+# periodic `receive` (staleness warnings appear otherwise); the signal-receive
+# timer handles that.
#
# Source: ~/code/rulesets/claude-templates/bin/agent-text
# Install: make -C ~/code/rulesets install
SIGNAL_ACCOUNT="+15045173983"
CRAIG_UUID="b1b5601e-6126-47f8-afaa-0a59f5188fde"
-VELOX_HOST="velox.tailf3bb8c.ts.net"
+# Relay hosts, tried in order until one sends. ratio leads because it is the
+# always-on desktop at home, while velox is the laptop that travels and sleeps.
+# Override for a one-off with AGENT_TEXT_RELAYS="host1 host2".
+AGENT_TEXT_RELAYS="${AGENT_TEXT_RELAYS:-ratio.tailf3bb8c.ts.net velox.tailf3bb8c.ts.net}"
if [ $# -eq 0 ]; then
echo "usage: agent-text <message...>" >&2
@@ -37,21 +44,34 @@ if [ $# -eq 0 ]; then
fi
msg="$*"
+self="$(uname -n)"
# The account is local if this machine's signal-cli holds it: the registered
-# primary (velox) or any linked device. Those send directly.
+# primary or any linked device. Those send directly.
if signal-cli listAccounts 2>/dev/null | grep -q "$SIGNAL_ACCOUNT"; then
signal-cli -a "$SIGNAL_ACCOUNT" send -m "$msg" "$CRAIG_UUID"
rc=$?
+ why="local signal-cli send failed"
else
- # printf %q hardens the message for the remote shell.
- ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new \
- "$VELOX_HOST" \
- "signal-cli -a $SIGNAL_ACCOUNT send -m $(printf '%q' "$msg") $CRAIG_UUID"
- rc=$?
+ rc=1
+ why="no relay reachable in: $AGENT_TEXT_RELAYS"
+ for host in $AGENT_TEXT_RELAYS; do
+ # Never relay to this machine. Reaching this branch means the account is
+ # NOT local, so an ssh round trip to ourselves lands on the same empty
+ # signal-cli and cannot succeed. velox hit exactly that after its
+ # 2026-08-13 reinstall wiped the registration: the only relay target was
+ # velox, so the fallback pointed at the one machine guaranteed to fail.
+ [ "${host%%.*}" = "${self%%.*}" ] && continue
+ # printf %q hardens the message for the remote shell.
+ ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new \
+ "$host" \
+ "signal-cli -a $SIGNAL_ACCOUNT send -m $(printf '%q' "$msg") $CRAIG_UUID"
+ rc=$?
+ [ "$rc" -eq 0 ] && break
+ done
fi
if [ "$rc" -ne 0 ]; then
- echo "agent-text: phone message failed (velox down or unreachable?); fall back to the desktop channel: notify info 'Message' '<message>' --persist" >&2
+ echo "agent-text: phone message failed ($why); fall back to the desktop channel: notify info 'Message' '<message>' --persist" >&2
fi
exit "$rc"