aboutsummaryrefslogtreecommitdiff
path: root/docs/design/2026-08-19-agent-text-relay-fallback-decision.org
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design/2026-08-19-agent-text-relay-fallback-decision.org')
-rw-r--r--docs/design/2026-08-19-agent-text-relay-fallback-decision.org155
1 files changed, 155 insertions, 0 deletions
diff --git a/docs/design/2026-08-19-agent-text-relay-fallback-decision.org b/docs/design/2026-08-19-agent-text-relay-fallback-decision.org
new file mode 100644
index 0000000..48dd572
--- /dev/null
+++ b/docs/design/2026-08-19-agent-text-relay-fallback-decision.org
@@ -0,0 +1,155 @@
+#+TITLE: agent-text relay fallback — decision record
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-08-19
+
+* What broke
+
+velox's 2026-08-13 reinstall wiped its =signal-cli= registration. Its account
+store came back empty.
+
+That alone would have cost velox one channel. =agent-text= made it cost the
+fleet, because the script hardcoded =VELOX_HOST= as its single ssh relay
+target. So a send from velox relayed to velox, hit the same empty store, and
+failed; and any third machine without a local account relayed into the same
+dead end. Only ratio still worked, and only because it holds the account as a
+linked device and takes the direct branch.
+
+The failure text said "velox down or unreachable?", which was actively
+misleading. velox was up and on the tailnet the whole time. It simply was not
+registered any more.
+
+* Why re-registering velox was not the fix
+
+velox held the *primary* registration, not a linked device, and its keys are
+gone. In Signal a linked device cannot authorize another device, so ratio
+(device 2) cannot link velox back. There is no QR to scan.
+
+Re-registering the number from velox would work, but it mints a new primary,
+drops ratio's link, and needs an SMS or voice code delivered to
++15045173983 — and if that code cannot be received, the identity is
+unrecoverable. That is real downside risk taken on to restore exactly the
+single-primary topology that had just proved fragile. I chose the relay
+repoint instead.
+
+* Two proposals, one chosen
+
+Both =.emacs.d= (2026-08-14) and archsetup (2026-08-16) diagnosed this
+independently and proposed replacing the constant with an ordered relay list.
+I took archsetup's, on one difference.
+
+=.emacs.d='s version tries each host in order and stops at the first success.
+On velox that works — but only because ratio happens to be listed first. It
+never skips *self*, so the failure it fixes is still reachable: a fleet whose
+list led with velox would relay velox to velox exactly as before. It repairs
+the instance rather than the defect.
+
+archsetup's adds the guard:
+
+: [ "${host%%.*}" = "$self" ] && continue
+
+Reaching the relay branch *means* the account is not local. So an ssh round
+trip to this machine lands on the same store that just came back empty and
+cannot succeed, whatever the list order. That is the defect; the ordering was
+the symptom.
+
+archsetup's also makes the list overridable (=AGENT_TEXT_RELAYS=), which is a
+genuine improvement over both the old constant and a fixed array — the fleet
+assumption can be stepped around per invocation without editing the script.
+
+* The question neither proposal had answered
+
+=.emacs.d= raised the sharpest point in either handoff and left it open: a
+relay host that is *reachable but unregistered* looks identical to success
+from the caller's side unless the exit code is checked. The loop breaks on
+=rc -eq 0=, so if =signal-cli send= exits zero against an empty account store,
+the loop reports success for a message that went nowhere — the worst possible
+outcome for a channel whose entire job is reaching me when I am away.
+
+Answered empirically before applying, on velox, which is itself the
+reachable-but-unregistered case:
+
+: $ signal-cli -a +15045173983 send -m ... <uuid>
+: User +15045173983 is not registered.
+: exit code: 1
+
+So the failure is clean and the loop advances correctly. Recorded in
+=protocols.org= so the next reader does not have to re-derive it.
+
+* What else was stale
+
+Fixing the script exposed that =protocols.org= "Reaching Craig" asserted the
+same dead fact — the account "is registered on velox (primary) and ratio
+(linked device)". Rewritten to state the *derivation* (any machine whose local
+=signal-cli= holds it sends directly; check with =signal-cli listAccounts=)
+rather than the snapshot, per the host-identity rule. The snapshot form is
+what rotted; a second snapshot would rot the same way at the next rebuild.
+
+The same section's =signal-mcp= claim was also false by then: velox's global
+=~/.claude.json= carries only =linear= and =slack-deepsat= after the reinstall.
+Softened to "may or may not be registered on the machine you're on".
+
+* What the review caught
+
+The guard I chose this proposal *for* had the same shape of flaw I rejected the
+other proposal over, and I did not see it. It read:
+
+: [ "${host%%.*}" = "$self" ] && continue
+
+That strips the domain off the candidate host but not off =uname -n=. Both
+machines return a short nodename today, so it worked — and nodename is just
+whatever =/etc/hostname= holds. One =hostnamectl set-hostname= to an FQDN on
+any machine and the guard silently stops firing, restoring the 2026-08-13 bug
+on the away channel with nothing to announce it. So the defect fix was itself
+instance-dependent, which is the exact criticism I had made of .emacs.d's
+version one section above. Both sides are stripped now, and the test that used
+to assert the direction that already worked was repointed at the direction that
+was broken.
+
+Three more, all correct and all fixed here:
+
+- The failure message named the relay list on *both* branches, so a local
+ =signal-cli= failure on the machine that actually holds the account — ratio,
+ the fleet's only working sender — would send a debugger chasing the tailnet
+ while the fault sat on the box in front of them. That is the same misdirection
+ as the old "velox down or unreachable?" text that this change exists to
+ remove. It now carries a per-branch reason.
+- The header's "Verified end to end ... 2026-08-16 (velox -> ratio, relay)" was
+ archsetup's verification, not mine, sitting in a list of two I could vouch for
+ and inheriting their credibility. Their handoff reports running the exact
+ relay command from velox and getting a Signal timestamp with rc 0. That is
+ real, and it is on report — the header now says so.
+- =protocols.org= and the script header both point at the runbook in
+ =docs/design/= as authoritative, and that runbook still asserted the retired
+ topology in four places. Correcting two surfaces and leaving the one they
+ route to is worse than not having started: the contradiction reads as a
+ disagreement between sources rather than one uniformly old story. The runbook
+ now opens with a dated superseded note, and =signal-receive.sh='s header
+ comment moved to the derivation form.
+
+* Verification
+
+- =bash -n= and =shellcheck -S warning= clean.
+- =agent-text.bats= 5 tests → 10, all green. The suite now stubs =uname= so the
+ self-skip is exercised deterministically instead of depending on which
+ machine runs it.
+- Mutation-proven rather than assumed. Deleting the self-relay guard reds tests
+ 3, 4 and 9; making the loop break unconditionally reds test 5; seeding =rc=0=
+ instead of =1= reds test 9. Each mutation is caught by a distinct test, so
+ the suite guards the behavior rather than merely covering the lines.
+- Full suite: only the pre-existing =ai-launcher-runtime.bats= codex failure
+ remains, which is a velox environment gap tracked separately.
+- The two review fixes are mutation-proven the same way rather than assumed:
+ reverting the self-side strip reds the FQDN test, and making the failure
+ message branch-blind again reds the local-failure test. Suite is 11 tests.
+
+* Provenance of the relay verification
+
+Recorded here because the working directory was filed away and this is the only
+surviving copy. archsetup's 2026-08-16 handoff states: "Verified from velox
+before proposing: signal-cli is at /usr/bin/signal-cli on ratio's
+non-interactive PATH, and the exact relay command agent-text would run returned
+a Signal timestamp with rc 0. Direct send from ratio also verified. bash -n and
+shellcheck -S warning are both clean on the attached file."
+
+That is the source for the header's 2026-08-16 line. I did not re-run a live
+relay send, because doing so delivers a real message to Craig's phone.