aboutsummaryrefslogtreecommitdiff
path: root/claude-templates
diff options
context:
space:
mode:
Diffstat (limited to 'claude-templates')
-rw-r--r--claude-templates/.ai/protocols.org18
-rw-r--r--claude-templates/.ai/workflows/INDEX.org2
-rw-r--r--claude-templates/.ai/workflows/page-me.org24
-rw-r--r--claude-templates/.ai/workflows/triage-intake.org21
-rw-r--r--claude-templates/.ai/workflows/work-the-backlog.org2
-rwxr-xr-xclaude-templates/bin/agent-page57
-rwxr-xr-xclaude-templates/bin/agent-text57
-rwxr-xr-xclaude-templates/bin/install-ai2
8 files changed, 113 insertions, 70 deletions
diff --git a/claude-templates/.ai/protocols.org b/claude-templates/.ai/protocols.org
index d89e345..3cd02ef 100644
--- a/claude-templates/.ai/protocols.org
+++ b/claude-templates/.ai/protocols.org
@@ -430,27 +430,29 @@ Full usage: =notify --help= or see =~/.local/bin/notify=
- =atq= - list all scheduled alarms
- =atrm [number]= - remove an alarm by its queue number
-** Paging Craig — the agent pager
+** Reaching Craig — the notification vocabulary
-"Page me" has two channels; pick by where Craig is. Both work from any agent runtime — nothing here is Claude-specific.
+Two channels, two trigger words. "page me" is the desktop, "text me" is the phone, "text and page me" is both. Pick by where Craig is, and default to both when a run can't tell. Both work from any agent runtime (nothing here is Claude-specific). The words are what Craig says; a run deciding on its own maps the same way (away run texts, at-desk run pages, unsure does both).
-- *At his laptop/desktop* — desktop =notify ... --persist= (above). It reaches him on the machine and stays up until dismissed.
+- *"page me" — at his laptop/desktop.* A desktop =notify ... --persist= that reaches him on the machine and stays up until dismissed.
#+begin_src bash
notify info "Title" "Message" --persist
#+end_src
-- *Away from his laptop/desktop* — page his phone over Signal with the *agent pager*:
+- *"text me" — away from his machine.* A Signal push to his phone via =agent-text=:
#+begin_src bash
- agent-page "Message for Craig's phone"
+ agent-text "Message for Craig's phone"
#+end_src
- =agent-page= (in =~/.local/bin= via the rulesets install) sends from the dedicated pager identity (+15045173983, registered in velox's signal-cli) to Craig's Signal account UUID, firing a normal mobile push. On velox it sends directly; on any other tailnet machine it ssh-relays the send to velox. Verified end to end 2026-07-13. Never page Craig's phone *number* — it reads as unregistered in Signal's directory; the script already 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. 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.
- Caveats: velox must be up and on the tailnet (the script says so and names the desktop fallback when the relay fails), and the signal-cli account wants a periodic =receive= — both tracked on the rulesets Signal-pager task, which owns the full runbook.
+ 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/=.
-On velox, Claude sessions may also have the *signal-mcp* tool (=send_message_to_user=, same pager identity) — fine to use there, but it exists only in velox's local MCP config, so =agent-page= is the portable habit. Do *not* use the old =page-signal= shell script (removed 2026-06-12).
+- *"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).
* Session Protocols
diff --git a/claude-templates/.ai/workflows/INDEX.org b/claude-templates/.ai/workflows/INDEX.org
index 02c4264..f18d953 100644
--- a/claude-templates/.ai/workflows/INDEX.org
+++ b/claude-templates/.ai/workflows/INDEX.org
@@ -113,7 +113,7 @@ This index must list every =.org= file in =.ai/workflows/= except this one and e
- Situational triggers: "broadcast the <event> to all projects", "broadcast that <situation>", "let every project know I'll be away ..."
- =flashcard-review.org= — review an org-drill flashcard file, restructure cards to question-form headings (no answer hints), audit content accuracy against project source-of-truth via subagent, rewrite source preserving SRS state, regenerate the Anki =.apkg= to =~/sync/phone/anki/=. Person cards use "Who is X? Tell me about their Y."; talking-points cards stay as-is. Script behavior: =flashcard-to-anki.py= strips =:PROPERTIES:= drawers + =SCHEDULED:= / =DEADLINE:= planning lines from Anki output.
- Triggers: "review the flashcards", "update the flashcards", "review the drill deck", "update the drill deck", "refresh the Anki cards", "let's run the flashcard-review workflow"
-- =page-me.org= — set a timed notification (desktop =notify=; phone via =agent-page= when Craig is away).
+- =page-me.org= — set a timed notification. "page me" desktop =notify=, "text me" phone via =agent-text=, "text and page me" both.
- Triggers: anything containing the word "page" used as a verb ("page me", "page me in 10 minutes", "page me at 3pm", "page my phone")
- =status-check.org= — proactive long-running-job updates.
- Triggers: "keep me posted on this", "provide status checks on this job", "let me know when it's done", "monitor this for me". Auto: any job estimated 10+ min.
diff --git a/claude-templates/.ai/workflows/page-me.org b/claude-templates/.ai/workflows/page-me.org
index bfa92c6..7a3b792 100644
--- a/claude-templates/.ai/workflows/page-me.org
+++ b/claude-templates/.ai/workflows/page-me.org
@@ -13,9 +13,17 @@ Uses the =notify= command (info type) for consistent notifications across all AI
Craig says *"page me"* (or variations like "page me in 10 minutes", "page me at 3pm").
-The word "page" is the trigger for this workflow. It means: set a timed notification.
+The word "page" is the trigger for this workflow. It means: set a timed notification on the *desktop* channel (=notify=).
-Previously called "set-alarm" -- renamed to "page-me" for a distinctive, short trigger phrase that won't collide with common words like "remind" or "alert."
+Two sibling triggers pick a different channel; the timed =at= machinery below is identical for all three, only the fired command changes:
+
+- *"page me"* — desktop =notify= (this workflow's default).
+- *"text me"* — a Signal push to Craig's phone via =agent-text= (the away channel).
+- *"text and page me"* — both, for when he might be either place.
+
+Scope the triggers to the reflexive "me": "page me" and "text me", not a bare "page" or "text" in prose. The full channel vocabulary lives in protocols.org "Reaching Craig".
+
+"page" was chosen (renamed from the old "set-alarm") for a distinctive, short trigger that won't collide with common words like "remind" or "alert".
* Problem We're Solving
@@ -113,18 +121,18 @@ notify info "Page" "Your message here" --persist
The =--persist= flag keeps the notification on screen until manually dismissed. All page-me notifications should use =--persist= by default.
-** Paging Craig's phone (away from the machine)
+** Texting Craig's phone (the "text me" channel)
-The timed =notify= alarm above is the desktop channel. When Craig is away from the machine (or asks to be paged "on my phone"), use the agent pager instead — a Signal push to his phone from any machine or agent runtime:
+The timed =notify= alarm above is the desktop channel. When Craig says "text me" (or a run expects him away from the machine), use =agent-text= instead, a Signal push to his phone from any machine or agent runtime:
#+begin_src bash
-agent-page "Build finished — ready for your eyes"
+agent-text "Build finished, ready for your eyes"
-# Timed phone page: same at-daemon pattern, different channel
-echo "agent-page 'Meeting starts in 5'" | at 3:25pm
+# Timed phone message: same at-daemon pattern, different channel
+echo "agent-text 'Meeting starts in 5'" | at 3:25pm
#+end_src
-Channel selection and the pager's mechanics live in protocols.org "Paging Craig — the agent pager". When in doubt, fire both: the desktop notification persists for whenever he returns, the phone push reaches him now.
+Channel selection and the mechanics live in protocols.org "Reaching Craig". On "text and page me", fire both: the desktop notification persists for whenever he returns, the phone push reaches him now.
** Managing Alarms
diff --git a/claude-templates/.ai/workflows/triage-intake.org b/claude-templates/.ai/workflows/triage-intake.org
index 7451fed..55cc939 100644
--- a/claude-templates/.ai/workflows/triage-intake.org
+++ b/claude-templates/.ai/workflows/triage-intake.org
@@ -209,6 +209,22 @@ Auto mode runs as a =/loop= in the *live session*, not a detached cron job:
Running in the live session means MCP auth (Slack, Gmail, Linear) is inherited from the session — the headless-auth wall that blocks a detached cron run does not apply. A durable cross-session schedule is out of scope here; that belongs to the morning-ops orchestrator, which can later invoke auto mode's accumulate behavior as its triage limb. The close/stop commands below require a live session by design.
+*** Phone delivery — push each signal sweep via =agent-text=
+
+Auto mode exists for when Craig is away from the desk, so a sweep that surfaces something worth seeing is delivered to his phone, not just printed into a session he isn't watching. After a sweep that renders the full three sections — one with real deltas or an unacked-list change (see "End-of-sweep output" below) — send that same output to his phone over Signal with =agent-text=:
+
+#+begin_src bash
+agent-text "$SWEEP_SUMMARY"
+#+end_src
+
+The pushed text is the *fuller* three-section shape, not a terse one-liner: the per-source deltas, the responses-awaiting-acknowledgment list, and the timestamp, led by a ⚠ SCAN FAILED banner if any source failed.
+
+*Signal-only — never on a quiet sweep.* An empty sweep (the =triage intake at HH:MM: nothing= heartbeat) does *not* push to the phone. Silent-until-signal (see =docs/specs/2026-07-20-silent-until-signal-monitors-spec.org=) governs the phone channel too, so the phone stays silent until a sweep has real signal. The in-session heartbeat still prints as proof the loop ran; the phone is reserved for something that actually needs Craig. (Craig's ruling, 2026-07-20: the higher-cost channel doesn't buzz with "nothing.")
+
+If =agent-text= isn't on =PATH=, fall back to inline delivery and say so once.
+
+*Reply polling is deferred.* The send half ships here; polling the phone for Craig's replies (the =phone-recv= half of the retired ntfy design) waits on the reply-correlation follow-up. With the Signal account linked on more than one device, a reply fans out to every device and neither knows which page it answers — that has to be resolved before auto mode reads replies back. Until then auto mode pushes but does not poll, and Craig acts on a pushed summary from wherever he picks it up.
+
** Preconditions and Close-out
Auto mode borrows the inbox monitor-mode gates (=inbox.org= monitor mode): do not start on a dirty worktree or a red test suite — a close's batch commit would otherwise sweep up unrelated changes — and leave the tree clean and green when the loop stops. Surface a blocker with inline numbered options per =interaction.md= and wait.
@@ -226,7 +242,7 @@ Each sweep runs Phase 0 (load *both* plugin dirs — the loud requirement still
** End-of-sweep output — three sections, or one heartbeat
-*Silent-until-signal (see =docs/specs/2026-07-20-silent-until-signal-monitors-spec.org=).* An *empty sweep* — no deltas since the previous sweep and no change to the awaiting-acknowledgment list — collapses to a single heartbeat line and nothing else: =triage intake at HH:MM: nothing= (HH:MM local, from =date=). Detection still runs in full (Phase 0 plus the A-D scan, against the session's inherited MCP auth); only the output collapses, so a long unattended run stops filling the session with identical "no changes" blocks. A sweep with real deltas or an unacked-list change prints the full three sections below.
+*Silent-until-signal (see =docs/specs/2026-07-20-silent-until-signal-monitors-spec.org=).* An *empty sweep* — no deltas since the previous sweep and no change to the awaiting-acknowledgment list — collapses to a single heartbeat line and nothing else: =triage intake at HH:MM: nothing= (HH:MM local, from =date=). Detection still runs in full (Phase 0 plus the A-D scan, against the session's inherited MCP auth); only the output collapses, so a long unattended run stops filling the session with identical "no changes" blocks. A sweep with real deltas or an unacked-list change prints the full three sections below, and — when away — pushes them to Craig's phone via =agent-text= (see "Phone delivery" above). The empty-sweep heartbeat is never pushed.
1. *Deltas* — what changed since the *previous sweep* (the standard Phase C summary scoped to the inter-sweep delta).
2. *Responses awaiting your acknowledgment* — every Slack reply, email, or message directed at Craig that he hasn't acknowledged or had the agent answer. A *running list carried forward across sweeps* until Craig acks each item or closes the triage. An away user's first need is "who's waiting to hear back from me," which a delta-only sweep loses the moment it scrolls past.
@@ -424,6 +440,9 @@ Update the engine as the orchestration pattern evolves; update a plugin as its s
*** Updates and Learnings
+**** 2026-07-20: Phone delivery for signal sweeps (=agent-text=, send half)
+Auto mode now pushes a full-three-section sweep to Craig's phone over Signal via =agent-text=, the away-from-desk delivery the retired ntfy design carried before ntfy was torn down (2026-07-04). Transport is =agent-text= (the renamed Signal pager), not ntfy. Signal-only by Craig's 2026-07-20 ruling: a quiet sweep's =nothing= heartbeat never reaches the phone — silent-until-signal governs the phone channel too, so the higher-cost channel only fires when a sweep has real signal, while the in-session heartbeat stays as proof the loop ran. Falls back to inline when =agent-text= is absent. Only the send half ships; reply polling (the old =phone-recv=) waits on the reply-correlation follow-up, because a Signal reply fans out to every linked device and neither knows which page it answers.
+
**** 2026-07-18: Three-section digest + close-by-default (Phase C/D rewrite)
Craig's ruling after a 42h-gap sweep where the long-form report (top signals + per-source breakdown + 7-option action menu) was followed by "summarize the notable items" — and the digest that answered it was the report he wanted first. Phase C now renders ==TASKS== (work items needing Craig, solo-executable first, priority order) / ==FYI== (work context, no action owed) / ==MISC== (everything outside the project, actions stated inline), then exactly two options (close-and-file / close-and-execute-solo, the latter only when solo items exist), timestamp last. Per-source blocks and the itemized action menu are gone from the default surface (long form on request). Phase D became the close: it runs as the next action no matter what Craig replies (unless he explicitly holds), includes the mail hygiene on every scanned account without itemized confirmation, files the TASKS, clears resolved unacked items, advances the sentinel, and tears down started services. Solo = mechanical + standing-approved + no prose under Craig's name; prose sends and destructive non-mail actions stay gated. The stay-open-until-confirmed exit loop is retired. Same-day addendum: the "and reroute" modifier ("1 and reroute") — MISC items are surfaced-only by default (never filed to this project's todo.org); appending the modifier delivers each outside-project item to its owner's inbox via inbox-send per the cross-project rule.
diff --git a/claude-templates/.ai/workflows/work-the-backlog.org b/claude-templates/.ai/workflows/work-the-backlog.org
index ab24d3b..3933749 100644
--- a/claude-templates/.ai/workflows/work-the-backlog.org
+++ b/claude-templates/.ai/workflows/work-the-backlog.org
@@ -152,7 +152,7 @@ With paging on, fire one page when the set is done or the cap is hit — end-of-
notify info "Page" "<project>: <N> done, <M> remaining — <one-line summary>" --persist
#+end_src
-=--persist= keeps it on screen until dismissed, and =info= is the page-me urgency convention (persistent but never crash-scary). The page fires when the set completes *or* the cap stops the run — either way exactly once. The message carries the project name, the completed count, and the remaining count (with skipped tasks noted in the run summary) so Craig can confirm ready and name the next project in one reply. =notify= is the desktop paging surface; a run that expects Craig to be away also fires =agent-page= with the same message (the Signal phone channel — protocols.org "Paging Craig — the agent pager").
+=--persist= keeps it on screen until dismissed, and =info= is the notification urgency convention (persistent but never crash-scary). The notification fires when the set completes *or* the cap stops the run, either way exactly once. The message carries the project name, the completed count, and the remaining count (with skipped tasks noted in the run summary) so Craig can confirm ready and name the next project in one reply. =notify= is the desktop channel (the "page me" surface); a run that expects Craig to be away also fires =agent-text= with the same message (the Signal phone channel, "text me"). See protocols.org "Reaching Craig".
* Metrics
diff --git a/claude-templates/bin/agent-page b/claude-templates/bin/agent-page
index 8e9b23c..728ee78 100755
--- a/claude-templates/bin/agent-page
+++ b/claude-templates/bin/agent-page
@@ -1,55 +1,12 @@
#!/bin/bash
-# agent-page — page Craig's phone over Signal, from any machine or agent runtime.
+# agent-page — deprecated alias for agent-text.
#
-# Usage: agent-page <message...>
-#
-# The pager 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 pager 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 page the number. Verified end to end 2026-07-13 (velox) and
-# 2026-07-20 (ratio, direct).
-#
-# This is the AWAY channel. At his desk, use the desktop channel instead:
-# notify info "Title" "Message" --persist
-# See protocols.org "Paging Craig" for choosing between them.
-#
-# Known caveats (tracked on the rulesets Signal-pager task, 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.
+# The Signal phone tool was renamed agent-text on 2026-07-20, when the
+# notification vocabulary split into "text me" (Signal) and "page me" (desktop).
+# This shim keeps old callers and other machines working until they re-install
+# and pick up agent-text directly. Remove it in a later cleanup once nothing
+# references agent-page.
#
# Source: ~/code/rulesets/claude-templates/bin/agent-page
-# Install: make -C ~/code/rulesets install
-
-PAGER_ACCOUNT="+15045173983"
-CRAIG_UUID="b1b5601e-6126-47f8-afaa-0a59f5188fde"
-VELOX_HOST="velox.tailf3bb8c.ts.net"
-
-if [ $# -eq 0 ]; then
- echo "usage: agent-page <message...>" >&2
- exit 2
-fi
-
-msg="$*"
-
-# The pager account is local if this machine's signal-cli holds it — the
-# registered primary (velox) or any linked device. Those send directly.
-if signal-cli listAccounts 2>/dev/null | grep -q "$PAGER_ACCOUNT"; then
- signal-cli -a "$PAGER_ACCOUNT" send -m "$msg" "$CRAIG_UUID"
- rc=$?
-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 $PAGER_ACCOUNT send -m $(printf '%q' "$msg") $CRAIG_UUID"
- rc=$?
-fi
-if [ "$rc" -ne 0 ]; then
- echo "agent-page: phone page failed (velox down or unreachable?) — fall back to the desktop channel: notify info 'Page' '<message>' --persist" >&2
-fi
-exit "$rc"
+exec "$(dirname "$(readlink -f "$0")")/agent-text" "$@"
diff --git a/claude-templates/bin/agent-text b/claude-templates/bin/agent-text
new file mode 100755
index 0000000..86aa933
--- /dev/null
+++ b/claude-templates/bin/agent-text
@@ -0,0 +1,57 @@
+#!/bin/bash
+# agent-text — text Craig's phone over Signal, from any machine or agent runtime.
+# The Signal half of the notification vocabulary: "text me" reaches the phone,
+# "page me" is the desktop channel (notify). See protocols.org "Reaching Craig".
+#
+# 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).
+#
+# 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.
+#
+# 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"
+
+if [ $# -eq 0 ]; then
+ echo "usage: agent-text <message...>" >&2
+ exit 2
+fi
+
+msg="$*"
+
+# The account is local if this machine's signal-cli holds it: the registered
+# primary (velox) 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=$?
+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=$?
+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
+fi
+exit "$rc"
diff --git a/claude-templates/bin/install-ai b/claude-templates/bin/install-ai
index 88cb8e5..3283c4a 100755
--- a/claude-templates/bin/install-ai
+++ b/claude-templates/bin/install-ai
@@ -2,7 +2,7 @@
# install-ai — PATH-facing launcher for the fresh-project bootstrapper.
#
# make install symlinks this into ~/.local/bin/install-ai (same bin loop that
-# links `ai` and `agent-page`), so `install-ai [--track|--gitignore] [PROJECT]`
+# links `ai` and `agent-text`), so `install-ai [--track|--gitignore] [PROJECT]`
# runs from anywhere. The real logic lives in scripts/install-ai.sh; this
# resolves its own location through the ~/.local/bin symlink and execs that
# script by its true repo path, so the script's own repo-root computation