aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-12 15:49:56 -0500
committerCraig Jennings <c@cjennings.net>2026-06-12 15:49:56 -0500
commitd8c38b0ac4d4b8885fe96230a234761187bd3f58 (patch)
tree4a5488754bdbca621424f79b255e0ac354c27843
parent13256aad033f84c0854f2d562685ea808a5ec619 (diff)
downloadrulesets-d8c38b0ac4d4b8885fe96230a234761187bd3f58.tar.gz
rulesets-d8c38b0ac4d4b8885fe96230a234761187bd3f58.zip
chore: drop the Signal triage-intake plugin
Remove the triage-intake Signal source plugin and de-list Signal from the engine's plugin enumeration. I'm rebuilding the Signal client (signel + signal-cli) from scratch, so the plugin would scan against an unstable client. The signal MCP server and its README setup stay. Re-add the plugin when the client is stable.
-rw-r--r--.ai/workflows/triage-intake.org4
-rw-r--r--.ai/workflows/triage-intake.signal.org116
-rw-r--r--claude-templates/.ai/workflows/triage-intake.org4
-rw-r--r--claude-templates/.ai/workflows/triage-intake.signal.org116
4 files changed, 4 insertions, 236 deletions
diff --git a/.ai/workflows/triage-intake.org b/.ai/workflows/triage-intake.org
index b804de1..b257f2d 100644
--- a/.ai/workflows/triage-intake.org
+++ b/.ai/workflows/triage-intake.org
@@ -48,7 +48,7 @@ The engine has no sources baked in. It discovers them by globbing *two* director
ls .ai/workflows/triage-intake.*.org .ai/project-workflows/triage-intake.*.org 2>/dev/null
#+end_src
-- =.ai/workflows/triage-intake.*.org= — *general* source plugins, template-synced (personal Gmail, personal calendar, cmail/Proton, Signal, Telegram, personal GitHub PRs).
+- =.ai/workflows/triage-intake.*.org= — *general* source plugins, template-synced (personal Gmail, personal calendar, cmail/Proton, Telegram, personal GitHub PRs).
- =.ai/project-workflows/triage-intake.*.org= — *PROJECT-SPECIFIC* source plugins, never synced, owned by this project (e.g. a work project's Linear, work Gmail, work Slack, enterprise-GitHub PRs).
⚠ *THE #1 FAILURE MODE — read this twice.* Globbing only =.ai/workflows/= and silently missing every project plugin. If you skip =.ai/project-workflows/=, the sweep runs with *half its sources* and Craig never learns what it dropped — the omission is invisible, because a missing source looks identical to a quiet source in the output. There is no error, no empty block, no warning. The sweep just lies by omission. *Glob both directories. Always.*
@@ -327,7 +327,7 @@ The sentinel is checked into git, but git tracks content, not mtime — so an mt
Craig, via the work project's same-day handoff: "we only need to report if anything's changed when we do triage intake." Sweep summaries report deltas only — a new invite, a new/moved/cancelled event, a new message needing attention. Unchanged sources get no block (the "Calendar — quiet" roll-call is retired), and an all-quiet sweep renders as a single "HH:MM sweep: no changes" line. Failures keep their loud banner (never folded into the no-change line) and the suggested-actions line stays when actions are queued. Same ruling: the telegram plugin's dev-community group traffic is dropped from reports entirely unless Craig asks (see that plugin's 2026-06-11 note).
**** 2026-06-10: Loud failure surfacing (Phase C item 0 + Common Mistake 9)
-Craig: "highlight any failures in daily triage loudly. I get important communication from all these channels." Trigger: the 2026-06-10 sweep shipped with Signal silently missing — a standalone receive hung on the account lock while the signel daemon owned it, and the failure looked identical to a quiet source. Failures now lead the summary in a ⚠ SCAN FAILED banner; the signal and telegram plugins' failure paths point at this rule.
+Craig: "highlight any failures in daily triage loudly. I get important communication from all these channels." Trigger: the 2026-06-10 sweep shipped with Signal silently missing — a standalone receive hung on the account lock while the signel daemon owned it, and the failure looked identical to a quiet source. Failures now lead the summary in a ⚠ SCAN FAILED banner; the telegram plugin's failure path points at this rule.
**** 2026-05-26: Refactor into engine + source plugins
Split the monolithic workflow into a source-agnostic engine (this file) and per-source plugins named =triage-intake.<source>.org=. The engine carries the anchor/sentinel logic, the four-bucket model, the Phase A-D orchestration, the todo.org persistence convention, and the exit criteria. Each source's scan/classify/render/action knowledge moved to its own plugin. General plugins (personal-gmail, personal-calendar, cmail, github-prs) live in =.ai/workflows/= and are template-synced; project-specific plugins (a work project's Linear, work Gmail, work Slack, enterprise PRs) live in the project's =.ai/project-workflows/= and are never synced. Phase 0 globs *both* directories — the loud requirement, because missing the project dir silently halves the sweep. Naming convention: first dot is the engine/plugin boundary, deeper dots reserved for sub-adapters. This removed all DeepSat/Linear specifics from the engine; they become work-project plugins.
diff --git a/.ai/workflows/triage-intake.signal.org b/.ai/workflows/triage-intake.signal.org
deleted file mode 100644
index 978241d..0000000
--- a/.ai/workflows/triage-intake.signal.org
+++ /dev/null
@@ -1,116 +0,0 @@
-#+TITLE: Triage Intake — Signal Source
-#+AUTHOR: Craig Jennings & Claude
-#+DATE: 2026-06-10
-
-# Source plugin for the triage-intake engine. See triage-intake.org for the
-# contract and the Phase A-D orchestration. This file declares ONE source.
-#
-# General (personal) source: Signal Private Messenger. Emacs is the ONLY way
-# to check Signal (Craig's ruling, 2026-06-10). His Emacs daemon runs
-# signal-cli in jsonRpc mode via the signel package (~/code/signel, wired in
-# ~/.emacs.d/modules/signal-config.el), and that daemon OWNS the account: it
-# drains the server queue continuously, writes messages into *Signel: <id>*
-# buffers, and fires desktop notifications.
-#
-# ⚠ NEVER run a standalone `signal-cli receive`. signel and standalone
-# signal-cli share the same account config and device queue, so a standalone
-# drain STEALS messages the Emacs client would otherwise show Craig — and if
-# the daemon is live it hangs on the account lock besides (observed
-# 2026-06-10: a backgrounded receive blocked 3+ minutes and the sweep shipped
-# without Signal). signal-cli's only legitimate direct uses are
-# queue-untouching reads like `signal-cli listAccounts`.
-
-* Source: signal
-:PROPERTIES:
-:ORDER: 22
-:ENABLED: command -v emacsclient
-:ANCHOR: none
-:SUBAGENT_OVER: 40
-:END:
-
-** Scan
-
-*** Step 0 — ensure signel is running (start it if down, leave it running)
-
-#+begin_src bash
-SIGNEL_LIVE=$(emacsclient -e "(and (featurep 'signel) (process-live-p (get-process \"signal-rpc\")) t)" 2>/dev/null)
-# Belt and suspenders — the raw process check catches a signel daemon even
-# if the elisp probe fails:
-pgrep -f "org.asamk.signal.Main.*jsonRpc" >/dev/null && SIGNEL_LIVE=t
-echo "signel daemon live: ${SIGNEL_LIVE:-nil}"
-#+end_src
-
-- =t= → proceed straight to the buffer query below.
-- =nil= → *start signel through Emacs* and leave it running — a live signel is the desired steady state (unlike telega's leave-no-trace lifecycle, there is no teardown step):
-
- #+begin_src bash
- emacsclient -e "(progn (require 'signel) (cj/signel--ensure-started))"
- sleep 5 # let queued envelopes land in the chat buffers
- #+end_src
-
-- emacsclient unreachable, or signel fails to start → SCAN FAILED. Surface loudly per the engine's failure rule; never report Signal as "quiet."
-
-*** Buffer query
-
-signel keeps three sources of truth: =signel--active-chats= (hash of chat-ids seen this Emacs session), =signel--contact-map= (number → display name), and the =*Signel: <id>*= chat buffers (message lines stamped =[HH:MM]=, senders as =<Name>=, Craig's own as =<Me>=).
-
-One emacsclient call returns every active chat with its name and recent buffer tail:
-
-#+begin_src bash
-emacsclient -e "(progn (require 'signel)
- (let (chats)
- (maphash (lambda (id _v)
- (push (list id (or (gethash id signel--contact-map) \"?\")
- (let ((b (get-buffer (format \"*Signel: %s*\" id))))
- (if b (with-current-buffer b
- (buffer-substring-no-properties (max (point-min) (- (point-max) 1500)) (point-max)))
- \"no buffer\")))
- chats))
- signel--active-chats)
- chats))"
-#+end_src
-
-Phase B reads the =[HH:MM]= stamps against the anchor to find what's new, and reads who spoke last: a thread whose last line is =<Me>= (or a closing acknowledgment from the contact) carries no reply owed; a thread ending on the contact's question does.
-
-Caveats, stated honestly in the render when they bite:
-- =signel--active-chats= covers only chats with traffic since the Emacs daemon (or signel) last started. It is not a full history.
-- A freshly-started signel shows only what arrives after the start plus the queued envelopes that land on connect. The phone and Signal Desktop still hold everything.
-- Timestamps are =[HH:MM]= with no date. Treat stamps as today's unless buffer position says otherwise.
-
-** Classify
-
-Bias: Signal is personal, conversational, and time-sensitive — it leans *Action*, because a direct message usually carries an implicit "respond." Volume is low, signal-to-noise high (the opposite of personal Gmail).
-
-- *Action:* an explicit ask, a question, a scheduling request, a reply owed to a person. A thread ending on the contact's unanswered question is the prime case.
-- *FYI:* a substantive message with no response owed — a link shared, a heads-up, a thread Craig already closed (last word =<Me>= or a contact's closing ack).
-- *Noise-keep / trash:* automated notifications, reactions, sync echoes of Craig's own sends. Tally only.
-
-Flag a message from a contact Craig is mid-thread with prominently — a dropped personal reply is the expensive miss here.
-
-** Render
-
-#+begin_example
-**Signal — N active chats, M with new traffic since anchor.** <one-line summary>
-- Action: <items, sender + gist, reply owed called out>
-- FYI: <threads with new traffic Craig already handled, terse>
-- Noise: <tally>
-- (Caveat line, only when relevant: "covers chats since signel started <when>")
-#+end_example
-
-Omit the block ONLY when the scan genuinely ran and found nothing. A scan that could not run is never "quiet" — it renders as a loud SCAN FAILED line at the top of the whole summary (engine rule).
-
-** Actions
-
-All through the signel daemon:
-
-- reply (programmatic) :: =emacsclient -e "(progn (require 'signel) (signel--send-rpc \"send\" '((message . \"<body>\") (recipient . [\"+1555...\"]))))"= — for a group, replace =recipient= with =(groupId . \"<id>\")=. Public-facing (goes out under Craig's name): run =/voice personal= first.
-- reply (interactive) :: =emacsclient -e "(cj/signel-message)"= pops Craig's contact picker + chat buffer for a considered reply by hand.
-- attach :: open the chat buffer, =signel-attach-file=.
-
-No mark-read step: the daemon already consumed the queue.
-
-** History
-
-- 2026-06-08: initial standalone-receive plugin.
-- 2026-06-10: rewrote around the signel discovery. Craig's Emacs daemon runs signal-cli jsonRpc via signel, which owns the account lock; a standalone receive hung a sweep silently. Added Step 0 detection, the dual-path split, and the never-quiet-on-failure rule. Craig: Signal carries important communication; failures surface loudly.
-- 2026-06-10 (correction, same day): Craig's ruling — Emacs is the ONLY way to check Signal. Path B (standalone draining receive) removed entirely: signel and standalone signal-cli share the same account config and device queue, so a standalone drain steals messages from the Emacs client. Step 0 now starts signel via =cj/signel--ensure-started= when it's down and leaves it running (running signel is the steady state; no teardown). signal-cli survives only for queue-untouching reads like =listAccounts=.
diff --git a/claude-templates/.ai/workflows/triage-intake.org b/claude-templates/.ai/workflows/triage-intake.org
index b804de1..b257f2d 100644
--- a/claude-templates/.ai/workflows/triage-intake.org
+++ b/claude-templates/.ai/workflows/triage-intake.org
@@ -48,7 +48,7 @@ The engine has no sources baked in. It discovers them by globbing *two* director
ls .ai/workflows/triage-intake.*.org .ai/project-workflows/triage-intake.*.org 2>/dev/null
#+end_src
-- =.ai/workflows/triage-intake.*.org= — *general* source plugins, template-synced (personal Gmail, personal calendar, cmail/Proton, Signal, Telegram, personal GitHub PRs).
+- =.ai/workflows/triage-intake.*.org= — *general* source plugins, template-synced (personal Gmail, personal calendar, cmail/Proton, Telegram, personal GitHub PRs).
- =.ai/project-workflows/triage-intake.*.org= — *PROJECT-SPECIFIC* source plugins, never synced, owned by this project (e.g. a work project's Linear, work Gmail, work Slack, enterprise-GitHub PRs).
⚠ *THE #1 FAILURE MODE — read this twice.* Globbing only =.ai/workflows/= and silently missing every project plugin. If you skip =.ai/project-workflows/=, the sweep runs with *half its sources* and Craig never learns what it dropped — the omission is invisible, because a missing source looks identical to a quiet source in the output. There is no error, no empty block, no warning. The sweep just lies by omission. *Glob both directories. Always.*
@@ -327,7 +327,7 @@ The sentinel is checked into git, but git tracks content, not mtime — so an mt
Craig, via the work project's same-day handoff: "we only need to report if anything's changed when we do triage intake." Sweep summaries report deltas only — a new invite, a new/moved/cancelled event, a new message needing attention. Unchanged sources get no block (the "Calendar — quiet" roll-call is retired), and an all-quiet sweep renders as a single "HH:MM sweep: no changes" line. Failures keep their loud banner (never folded into the no-change line) and the suggested-actions line stays when actions are queued. Same ruling: the telegram plugin's dev-community group traffic is dropped from reports entirely unless Craig asks (see that plugin's 2026-06-11 note).
**** 2026-06-10: Loud failure surfacing (Phase C item 0 + Common Mistake 9)
-Craig: "highlight any failures in daily triage loudly. I get important communication from all these channels." Trigger: the 2026-06-10 sweep shipped with Signal silently missing — a standalone receive hung on the account lock while the signel daemon owned it, and the failure looked identical to a quiet source. Failures now lead the summary in a ⚠ SCAN FAILED banner; the signal and telegram plugins' failure paths point at this rule.
+Craig: "highlight any failures in daily triage loudly. I get important communication from all these channels." Trigger: the 2026-06-10 sweep shipped with Signal silently missing — a standalone receive hung on the account lock while the signel daemon owned it, and the failure looked identical to a quiet source. Failures now lead the summary in a ⚠ SCAN FAILED banner; the telegram plugin's failure path points at this rule.
**** 2026-05-26: Refactor into engine + source plugins
Split the monolithic workflow into a source-agnostic engine (this file) and per-source plugins named =triage-intake.<source>.org=. The engine carries the anchor/sentinel logic, the four-bucket model, the Phase A-D orchestration, the todo.org persistence convention, and the exit criteria. Each source's scan/classify/render/action knowledge moved to its own plugin. General plugins (personal-gmail, personal-calendar, cmail, github-prs) live in =.ai/workflows/= and are template-synced; project-specific plugins (a work project's Linear, work Gmail, work Slack, enterprise PRs) live in the project's =.ai/project-workflows/= and are never synced. Phase 0 globs *both* directories — the loud requirement, because missing the project dir silently halves the sweep. Naming convention: first dot is the engine/plugin boundary, deeper dots reserved for sub-adapters. This removed all DeepSat/Linear specifics from the engine; they become work-project plugins.
diff --git a/claude-templates/.ai/workflows/triage-intake.signal.org b/claude-templates/.ai/workflows/triage-intake.signal.org
deleted file mode 100644
index 978241d..0000000
--- a/claude-templates/.ai/workflows/triage-intake.signal.org
+++ /dev/null
@@ -1,116 +0,0 @@
-#+TITLE: Triage Intake — Signal Source
-#+AUTHOR: Craig Jennings & Claude
-#+DATE: 2026-06-10
-
-# Source plugin for the triage-intake engine. See triage-intake.org for the
-# contract and the Phase A-D orchestration. This file declares ONE source.
-#
-# General (personal) source: Signal Private Messenger. Emacs is the ONLY way
-# to check Signal (Craig's ruling, 2026-06-10). His Emacs daemon runs
-# signal-cli in jsonRpc mode via the signel package (~/code/signel, wired in
-# ~/.emacs.d/modules/signal-config.el), and that daemon OWNS the account: it
-# drains the server queue continuously, writes messages into *Signel: <id>*
-# buffers, and fires desktop notifications.
-#
-# ⚠ NEVER run a standalone `signal-cli receive`. signel and standalone
-# signal-cli share the same account config and device queue, so a standalone
-# drain STEALS messages the Emacs client would otherwise show Craig — and if
-# the daemon is live it hangs on the account lock besides (observed
-# 2026-06-10: a backgrounded receive blocked 3+ minutes and the sweep shipped
-# without Signal). signal-cli's only legitimate direct uses are
-# queue-untouching reads like `signal-cli listAccounts`.
-
-* Source: signal
-:PROPERTIES:
-:ORDER: 22
-:ENABLED: command -v emacsclient
-:ANCHOR: none
-:SUBAGENT_OVER: 40
-:END:
-
-** Scan
-
-*** Step 0 — ensure signel is running (start it if down, leave it running)
-
-#+begin_src bash
-SIGNEL_LIVE=$(emacsclient -e "(and (featurep 'signel) (process-live-p (get-process \"signal-rpc\")) t)" 2>/dev/null)
-# Belt and suspenders — the raw process check catches a signel daemon even
-# if the elisp probe fails:
-pgrep -f "org.asamk.signal.Main.*jsonRpc" >/dev/null && SIGNEL_LIVE=t
-echo "signel daemon live: ${SIGNEL_LIVE:-nil}"
-#+end_src
-
-- =t= → proceed straight to the buffer query below.
-- =nil= → *start signel through Emacs* and leave it running — a live signel is the desired steady state (unlike telega's leave-no-trace lifecycle, there is no teardown step):
-
- #+begin_src bash
- emacsclient -e "(progn (require 'signel) (cj/signel--ensure-started))"
- sleep 5 # let queued envelopes land in the chat buffers
- #+end_src
-
-- emacsclient unreachable, or signel fails to start → SCAN FAILED. Surface loudly per the engine's failure rule; never report Signal as "quiet."
-
-*** Buffer query
-
-signel keeps three sources of truth: =signel--active-chats= (hash of chat-ids seen this Emacs session), =signel--contact-map= (number → display name), and the =*Signel: <id>*= chat buffers (message lines stamped =[HH:MM]=, senders as =<Name>=, Craig's own as =<Me>=).
-
-One emacsclient call returns every active chat with its name and recent buffer tail:
-
-#+begin_src bash
-emacsclient -e "(progn (require 'signel)
- (let (chats)
- (maphash (lambda (id _v)
- (push (list id (or (gethash id signel--contact-map) \"?\")
- (let ((b (get-buffer (format \"*Signel: %s*\" id))))
- (if b (with-current-buffer b
- (buffer-substring-no-properties (max (point-min) (- (point-max) 1500)) (point-max)))
- \"no buffer\")))
- chats))
- signel--active-chats)
- chats))"
-#+end_src
-
-Phase B reads the =[HH:MM]= stamps against the anchor to find what's new, and reads who spoke last: a thread whose last line is =<Me>= (or a closing acknowledgment from the contact) carries no reply owed; a thread ending on the contact's question does.
-
-Caveats, stated honestly in the render when they bite:
-- =signel--active-chats= covers only chats with traffic since the Emacs daemon (or signel) last started. It is not a full history.
-- A freshly-started signel shows only what arrives after the start plus the queued envelopes that land on connect. The phone and Signal Desktop still hold everything.
-- Timestamps are =[HH:MM]= with no date. Treat stamps as today's unless buffer position says otherwise.
-
-** Classify
-
-Bias: Signal is personal, conversational, and time-sensitive — it leans *Action*, because a direct message usually carries an implicit "respond." Volume is low, signal-to-noise high (the opposite of personal Gmail).
-
-- *Action:* an explicit ask, a question, a scheduling request, a reply owed to a person. A thread ending on the contact's unanswered question is the prime case.
-- *FYI:* a substantive message with no response owed — a link shared, a heads-up, a thread Craig already closed (last word =<Me>= or a contact's closing ack).
-- *Noise-keep / trash:* automated notifications, reactions, sync echoes of Craig's own sends. Tally only.
-
-Flag a message from a contact Craig is mid-thread with prominently — a dropped personal reply is the expensive miss here.
-
-** Render
-
-#+begin_example
-**Signal — N active chats, M with new traffic since anchor.** <one-line summary>
-- Action: <items, sender + gist, reply owed called out>
-- FYI: <threads with new traffic Craig already handled, terse>
-- Noise: <tally>
-- (Caveat line, only when relevant: "covers chats since signel started <when>")
-#+end_example
-
-Omit the block ONLY when the scan genuinely ran and found nothing. A scan that could not run is never "quiet" — it renders as a loud SCAN FAILED line at the top of the whole summary (engine rule).
-
-** Actions
-
-All through the signel daemon:
-
-- reply (programmatic) :: =emacsclient -e "(progn (require 'signel) (signel--send-rpc \"send\" '((message . \"<body>\") (recipient . [\"+1555...\"]))))"= — for a group, replace =recipient= with =(groupId . \"<id>\")=. Public-facing (goes out under Craig's name): run =/voice personal= first.
-- reply (interactive) :: =emacsclient -e "(cj/signel-message)"= pops Craig's contact picker + chat buffer for a considered reply by hand.
-- attach :: open the chat buffer, =signel-attach-file=.
-
-No mark-read step: the daemon already consumed the queue.
-
-** History
-
-- 2026-06-08: initial standalone-receive plugin.
-- 2026-06-10: rewrote around the signel discovery. Craig's Emacs daemon runs signal-cli jsonRpc via signel, which owns the account lock; a standalone receive hung a sweep silently. Added Step 0 detection, the dual-path split, and the never-quiet-on-failure rule. Craig: Signal carries important communication; failures surface loudly.
-- 2026-06-10 (correction, same day): Craig's ruling — Emacs is the ONLY way to check Signal. Path B (standalone draining receive) removed entirely: signel and standalone signal-cli share the same account config and device queue, so a standalone drain steals messages from the Emacs client. Step 0 now starts signel via =cj/signel--ensure-started= when it's down and leaves it running (running signel is the steady state; no teardown). signal-cli survives only for queue-untouching reads like =listAccounts=.