From ca1bb871417afafc319c115d6ae69fa8cd3fe53c Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Wed, 19 Aug 2026 15:08:34 -0700 Subject: docs: file the signal-pager and triage findings Nine tasks filed, from four senders and my own work. Two are [#A]: the triage github-prs precondition passes on a work credential, so a personal sweep returns work PRs, and the pager receive timer on ratio, which is now fixed. Three of the filings are one bug wearing different clothes. A guard that can't tell "nothing to do" from "couldn't do it" reports success either way. It showed up in the receive timer, in the triage engine, and in a test that went green at exit 127 against a path that didn't exist. Worth fixing as a class rather than three times. --- .ai/notes.org | 2 +- ...15-08-agent-text-relay-fix-and-pager-outage.org | 294 +++++++++++++++++++++ 2 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 .ai/sessions/2026-08-19-15-08-agent-text-relay-fix-and-pager-outage.org (limited to '.ai') diff --git a/.ai/notes.org b/.ai/notes.org index be6708c..734dd1c 100644 --- a/.ai/notes.org +++ b/.ai/notes.org @@ -85,6 +85,6 @@ Format: Markers maintained by workflows to record when they last ran. Read by other workflows that gate their behavior on freshness. :LAST_AUDIT: 2026-07-20 (open set current — this session's shipped work (working/temp, triage-source-activation, silent-until-signal, suspend detach) closed as it went; sentry cluster consolidated (merged the /schedule tasks, added cross-host-coordination); nothing shipped-but-open per git reconcile. Live finding: the Polyglot + Subprojects scouting tasks are SCHEDULED 2026-07-20 and due.) -:LAST_INBOX_PROCESS: 2026-08-04 (ten handoffs, batch-approved: four implemented — signature paths, lint-org anchor scope, gmail pagination floor, telega 404 callback; two folded — work's link sweep as independent confirmation, KB orphans 42 → 113; four filed — [#B] teardown live-sibling gate, [#B] agent-scoped anchor default, [#C] install-lang track-mode ignores, [#C] hook message names a step not a command; all four senders replied to) +:LAST_INBOX_PROCESS: 2026-08-19 (14 items, 4 real: ten byte-identical kb-hygiene reports deleted as script-sourced and the duplicate delivery filed [#C]; agent-text relay fix parked as a [#B] VERIFY with the prepared diff in working/agent-text-relay-fallback/ — archsetup's version taken over .emacs.d's competing one for its self-relay guard, bug confirmed live on velox; settings.json /model churn filed [#B]; ratio-only bats failures filed [#C] after the suite came back green here; inbox-send's inability to reach ~/.emacs.d found while replying and filed [#C]. Both senders replied to.) Format: one =:MARKER: YYYY-MM-DD= line per workflow. Workflows overwrite their own marker on completion. diff --git a/.ai/sessions/2026-08-19-15-08-agent-text-relay-fix-and-pager-outage.org b/.ai/sessions/2026-08-19-15-08-agent-text-relay-fix-and-pager-outage.org new file mode 100644 index 0000000..0599d90 --- /dev/null +++ b/.ai/sessions/2026-08-19-15-08-agent-text-relay-fix-and-pager-outage.org @@ -0,0 +1,294 @@ +#+TITLE: Session Context — 2026-08-19 +#+AUTHOR: Craig Jennings + +* Summary + +** Active Goal + +Startup found fourteen inbox items and a blocked rulesets pull. The session +became one long inbox pass plus the shared-asset change it surfaced: fix +=agent-text=, whose hardcoded relay target had died with velox's registration. + +** Decisions + +- *Took archsetup's proposal over .emacs.d's* for the same bug. Both replaced + the hardcoded host with an ordered list; only archsetup's skips a candidate + matching =uname -n=. .emacs.d's works on velox by accident of listing ratio + first, so it repairs the instance and leaves the defect reachable. +- *Measured rather than reasoned, three times, and it paid every time.* + .emacs.d's rc-0 worry, archsetup's escalation of it into a blocking =[#B]=, + and my own claim about =signal-receive.sh= no-opping cleanly all dissolved or + narrowed under one command. +- *Fixed the runbook rather than deferring it.* Correcting =protocols.org= while + leaving the document it points at asserting the dead topology is worse than + not starting, because the contradiction reads as sources disagreeing. +- *Removed ratio's stale signal-cli symlink* rather than pinning the unit's + path, so the packaged 0.14.7 wins for every caller instead of just the timer. + Left the 107M install tree — reclaiming it is Craig's call. +- *Committed the =/model= pin as-is* on Craig's ruling. It records a deliberate + choice (Opus 5 with the 1M-context variant, replacing a floating alias) and + clears the blocked pull, but does not fix the write-target problem. + +** Data Collected / Findings + +*The away channel was broken in two independent ways, and both reported success.* +=agent-text= relayed velox to velox after the reinstall. Separately, ratio's +=signal-receive= timer had been exiting =status=0/SUCCESS= while receiving +nothing, because =~/.local/bin/signal-cli= (0.14.5, manual, 2026-06-12) shadowed +the packaged 0.14.7 and refused on an upgraded database. Removing the symlink +fixed it; the service then drained real queued envelopes including a receipt +from Craig dated 2026-08-16. + +*I mis-attributed a number in my own task and corrected it.* I wrote that the +pager account had not received in 17 days. That staleness belonged to the +*personal* account =+15103169357=, proven by draining it and watching the +warning clear. =listAccounts= prints the warning above the account list without +naming which account it concerns. + +*The isolated reviewer earned the gate three rounds running.* It found that my +self-skip guard compared a domain-stripped candidate against an unstripped +=uname -n=, so an FQDN nodename silently restores the original bug — the same +"fixes the instance, not the defect" flaw I had written into the decision record +as my reason for rejecting the other proposal. My tests could not catch it +either: the =uname= stub only fed short names, so I had tested the direction +that already worked. It then caught my SUPERSEDED banner vouching for four +screens of runbook I had not read. Both are errors no self-review reaches. + +*It also found the live outage above*, while verifying a claim I had made in +prose, and reported its own isolation-run artifact rather than only the +corrected conclusion. + +*One failure shape, four instances.* A check that cannot distinguish "nothing to +do" from "could not do it" reports success either way: +=signal-receive.sh='s guard, =triage-intake='s missing ACTIONS-UNAVAILABLE +outcome, the reviewer's bats variant going green at exit 127, and the +=gh auth status= precondition passing on a work credential. + +*Confirmed home's two triage defects rather than trusting them*, and defect 1 is +worse here: velox's only configured GitHub host *is* =deepsat.ghe.com=, so a +personal sweep would return entirely work PRs. + +** Files Modified + +- =62a7fbf= =fix(agent-text): relay through a host that holds the account= — the + script, its suite (5 tests → 11, mutation-proven), =protocols.org= + mirror, + the pager runbook, =signal-receive.sh='s header, and a decision record. +- =.claude/settings.json= — the =/model= pin, committed separately. +- =todo.org= — nine tasks filed, two closed. +- ratio (no commit): removed the stale =~/.local/bin/signal-cli= symlink. + +KB: promoted 2 / consulted yes + +** Next Steps + +1. *=[#A]= github-prs returns work PRs into personal triage.* Pin both the + precondition (=--hostname github.com=) and the scan (=GH_HOST=). Safe + overnight only because it fires on a triage sweep — don't run one first. +2. *Fix the guard class, not three guards.* =signal-receive.sh= =[#B]= and + =triage-intake= =[#B]= are the same bug, and a sweep of =scripts/= for + =2>/dev/null= in front of a decision would find the rest. This is the highest + -value item on the list. +3. *=[#B]= the settings.json write target* — today's commit cleared the tree + without fixing why it dirties. Both options are in the task, unchosen. +4. Craig's two standing reminders are untouched and both need his decisions: the + context-engineering rightsizing and the sentry spec read, now five weeks old. +5. Nine unstamped =[#D]= tasks want a real task-review pass, not a sweep — the + staleness count is 40. + +* Session Log + +** 2026-08-19 Wed @ 12:59 — Startup + +Ran the startup workflow. Phase A.0 pulled nothing: the rulesets pull was +blocked by an unstaged =.claude/settings.json= (=model= flipped from =opus= to +=claude-opus-5[1m]=), which is the eleventh instance of the churn .emacs.d +handed off on 2026-08-14. =make install= had nothing new to link; the project +repo was already current. + +Phase A found 14 inbox items, 40 top-level tasks unreviewed past a week, 6 roam +inbox items (none rulesets'), and a clean host-identity/spec-sort probe. No +crashed-session anchor — the last session wrapped cleanly. + +Verified during Phase C prep, on velox (=uname -n=): + +- velox's =signal-cli listAccounts= is empty, so the =agent-text= relay bug the + two senders reported is live on this machine right now — a "text me" from + this session would relay to velox itself and fail. +- =bats= IS present here (=/usr/bin/bats=), so .emacs.d's 2026-08-14 "bats + absent on velox" finding is stale. +- The ten =kb-hygiene-report.org= inbox files are byte-identical (same md5), so + that is duplicate delivery, not ten distinct reports. + +** 2026-08-19 Wed @ 13:05 — Inbox processed to zero + +The Stop hook caught me presenting dispositions instead of applying them, which +was the right catch: startup Phase C says inbox processing is mandatory, not a +thing to ask about. Ran it properly. + +Ten kb-hygiene reports deleted as script-sourced, per the disposition ladder. +They were byte-identical, and their content (142 agent nodes, ~114 orphans) is +already carried by the =[#C]= KB orphan-node task from 2026-08-04. The +duplication itself is the finding, so it got its own =[#C]=: =kb-hygiene.sh= +names each report by timestamp, so nothing dedups, and whatever drives it ran +it ten times in four hours. + +The two agent-text handoffs are one item from two senders. archsetup's finished +script beats .emacs.d's diff on one point, and it is the point: the self-relay +guard. .emacs.d replaced the hardcoded host with an ordered list but never +skipped self, so their fix works on velox only because ratio happens to lead +the list. Parked as a =[#B]= VERIFY rather than applied, because agent-text is +a synced shared asset — prepared diff, both source proposals, and the written +skeptical review are in =working/agent-text-relay-fallback/=. The proposed file +is =bash -n= and =shellcheck -S warning= clean. Left as an apply condition: the +reachable-but-unregistered relay case that neither sender tested, where a +zero exit from =signal-cli send= would make the loop report success for a +message that went nowhere. + +Filed =[#B]= for the settings.json =/model= churn (eleventh flip, nothing +tracked before now, both of .emacs.d's options recorded unchosen since it is +Craig's call). Filed =[#C]= for the ratio bats failures after running the suite +here and getting 36 green — so it is ratio-local, not a code defect, and +.emacs.d's fleet-wide framing was too broad. + +Found a new bug while replying: =inbox-send= discovers only =~/projects= and +=~/code=, so =~/.emacs.d= is unreachable by name despite the dot-stripped +resolution existing in the script. =.emacs.d= can send to rulesets but rulesets +cannot answer it. Worked around with =INBOX_SEND_ROOTS= to get the reply out, +and filed =[#C]=. + +Both senders replied to. Inbox at zero. + +** 2026-08-19 Wed @ 14:35 — Applied the agent-text fix; refuted archsetup's follow-up + +Craig approved the parked fix. Applied archsetup's script to the canonical, +=bash -n= and =shellcheck= clean. + +Answered the open question first, because it gated the apply: on velox — which +is itself the reachable-but-unregistered case — =signal-cli send= exits 1 with +"User +15045173983 is not registered." So the loop advances rather than +reporting a phantom delivery. + +Rewrote the bats suite, 5 tests → 10. The important change is that it now stubs +=uname=, so the self-skip is exercised deterministically instead of depending on +which machine runs the suite; the old test 2 asserted on velox by name and would +have passed or failed by host. Mutation-proved the suite rather than assuming +it: deleting the self-relay guard reds tests 3, 4 and 9; an unconditional +=break= reds 5; seeding =rc=0= reds 9. Three mutations, three distinct catches. + +Rewrote protocols.org "Reaching Craig". It asserted the account is "registered +on velox (primary) and ratio (linked device)" — the exact fact velox's rebuild +destroyed. Replaced the snapshot with the derivation (=signal-cli listAccounts=) +per the host-identity rule, since a fresh snapshot would rot at the next +rebuild. Its =signal-mcp= claim was false too: velox's =~/.claude.json= now +holds only linear and slack-deepsat. + +Green baseline was not green — =ai-launcher-runtime.bats= test 8 fails here +because it asserts on =codex= being installed and codex did not survive the +reinstall. Triaged and filed =[#C]= rather than fixed, so the bar for this work +is "only that one remains". It is the same class as the ratio failures: a +characterization test reading the host instead of a fixture. + +*Refuted archsetup's follow-up.* They replied asserting the rc-0 case is a real +defect in what they sent — that an unregistered relay exits zero, so the loop +breaks and reports success. That is inference and it is wrong. Measured it two +ways: ssh propagates a remote exit code faithfully (=exit 7= came back as 7), +and =signal-cli= on ratio for an account it does not hold exits 1 with "User +... is not registered". Used a bogus number for the remote probe so nothing +could reach Craig by accident. Their =:blocked:= tag rests on a premise that +does not hold, so the reply has to carry the measurement, not an opinion. + +** 2026-08-19 Wed @ 14:36 — archsetup confirmed the refutation + +archsetup reproduced the measurement independently rather than taking my word +for it, then closed their =[#B]=, dropped the =:blocked:= tag, and named the +process failure themselves: my handoff had stated .emacs.d's concern as a +conditional ("if =signal-cli send= exits zero against an empty account store"), +and they converted that "if" into a graded defect with a blocking tag on +another project without running the one command that settles it — on the +machine that was the exact case. + +Worth keeping as the general lesson, since this is twice in one session: a +defect filed from reading code is a hypothesis. The measurement was one command +away on the machine in front of both of us. Nothing is owed in either +direction; the tag is gone and the dependency is clear. + +** 2026-08-19 Wed @ 15:05 — Review rounds 2 and 3 + +Round 1 returned Request Changes with four Important findings. All four were +right and I took every one without pushback. + +The sharpest was the self-skip guard — the thing I chose archsetup's proposal +*for*. It compared a domain-stripped candidate against an unstripped +=uname -n=, so an FQDN nodename silently disables it and restores the +2026-08-13 bug on the away channel. That is the same "fixes the instance, not +the defect" flaw I had written into the decision record as my reason for +rejecting .emacs.d's version, one section above my own instance-dependent fix. +My test suite could not catch it either: the =uname= stub I added only ever fed +short names, so I had tested the direction that already worked. + +Also fixed: the failure message named the relay list on both branches (so a +local failure on the only working sender would send a debugger chasing the +tailnet); the header's 2026-08-16 end-to-end claim was archsetup's, not mine, +and is now attributed on-report; and =protocols.org= pointed readers at a +runbook still asserting the retired topology, which is worse than not starting. + +Round 2 closed all four and found one more, and it is the one worth recording. +My SUPERSEDED banner on the runbook said "the operational recipes further down +still hold — only the topology section rotted." I had read the first 32 lines +of that file. Four sites below contradicted the vouch, including both +reply-drain recipes and a copy-pasteable =ssh velox= relay sitting under "reach +for the raw form when debugging". A wrong vouch is worse than no vouch: it +converts a reader's suspicion into confidence. + +Round 3 takes the stronger fix — the clause is gone and five dated notes sit +inline at each stale site, so nobody has to carry the banner four screens. + +The reviewer also ran a mutation I had not asked for, on a hazard my own fix +introduced: swapping =rc=$?= and =why== in the direct branch so =rc= captures +the assignment. Test 10 catches it, but incidentally — via the =why= text +rather than any assertion about =rc=. I asked whether that deserves a test that +fails for the right reason instead of relying on the incidental catch. + +Three times today a confident claim dissolved on inspection: .emacs.d's rc-0 +worry, archsetup's escalation of it, and my own guard. I measured the first two. +The third I would have shipped. + +*Loop bound:* this is round 3 of the three the publish flow allows. Another +Request Changes stops the loop and goes to Craig rather than spending a fourth. + +** 2026-08-19 Wed @ 15:20 — Review approved; residuals closed + +Round 3 came back *Approve*. The reviewer also settled my test-10 question by +isolation rather than by reading: it split the test into two variants, one +keeping only the exit-code assertion and one keeping only the text assertions, +and ran each against the mutated script. Both red independently, so +=[ "$status" -ne 0 ]= is a first-class direct-branch exit-propagation assertion +and not an incidental catch. No extra test needed — it would restate one that +already exists. + +The valuable part is that its *first* isolation run gave the opposite answer, +and it reported that rather than just the corrected conclusion. It had written +the variant bats files into a scratch directory, so =setup()='s =REPO_ROOT= +walked up from the wrong place and =$PAGE= pointed at a file that does not +exist. bash exited 127, and 127 satisfies =-ne 0=, so the variant went green +while never executing the script under test. + +*KB promotion candidate (update, not a new node).* That is the +already-recorded "A check that cannot fail proves nothing" class +(=20260808040531=), but it sharpens the node's own remedy. The node says +asserting =status= alongside output is what makes absence register — today a +=status= assertion was itself satisfied by absence, because 127 clears +=-ne 0=. The refinement: a *negative* assertion needs a positive control, or +infrastructure failure reads as the behavior under test. Append to that node at +wrap rather than writing a duplicate. + +Closed the four residuals the approval left open rather than filing them, since +I was already in the file: the "Linking a device" recipe is now marked as +currently impossible (the primary's keys are gone, a linked device cannot +authorize another) with the re-registration tradeoff and a pointer to the +decision record; plus the three wording minors. Sent back as a confirm-only +pass, because the approval predates those edits and I would rather not commit a +diff the reviewer has not seen. Flagged two of my own claims in it for checking +— that =signal-receive.sh= no-ops cleanly without the account, and that no +machine can currently run =addDevice= — since both are reasoned rather than +run, which is the error this session keeps making. -- cgit v1.2.3