aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ai/workflows/triage-intake.telegram.org55
-rw-r--r--claude-templates/.ai/workflows/triage-intake.telegram.org55
2 files changed, 86 insertions, 24 deletions
diff --git a/.ai/workflows/triage-intake.telegram.org b/.ai/workflows/triage-intake.telegram.org
index d855fcf..a318f12 100644
--- a/.ai/workflows/triage-intake.telegram.org
+++ b/.ai/workflows/triage-intake.telegram.org
@@ -157,14 +157,24 @@ stays non-nil). =telega-server-kill= is what actually stops the server. Call
left in =docker ps=. Skipping this whole branch when =TELEGA_WAS_RUNNING= is t is
the point of Step 0: never tear down a session Craig is actively using.
-⚠ *SEGFAULT GOTCHA — docker mode is the fix.* The tdlib =telega-server= can exit
-abnormally (code 139, SIGSEGV) during or just after the initial sync. Observed
-on the 2026-06-09 first run; the cure was *docker mode* — =telega-use-docker= = t
-with the =zevlg/telega-server:latest= image (already pulled on Craig's box).
-Restarted under docker, the server reached "Ready" in ~2s, synced 73 chats, and
-held through a live re-scan with no crash. So: *ensure docker mode is on before
-the sweep.* If =telega-use-docker= is nil, set it (=(setq telega-use-docker t)=)
-and restart telega.
+⚠ *SEGFAULT GOTCHA — crashes are spontaneous; treat server death as routine.*
+The dockerized =telega-server= (=zevlg/telega-server:latest=, image built
+2026-06-04, tdlib 1.8.64) SIGSEGVs (exit 139) *on its own*, minutes-to-hours
+into a session — 11 host coredumps between 2026-06-09 and 2026-06-11, several at
+times when no triage verb was running. The 2026-06-11 investigation reproduced
+the crash-free verbs and the spontaneous deaths side by side: coredump
+backtraces show a corrupted stack (memory corruption in the musl build), and
+no newer image exists upstream. Earlier theories — "native mode is the trigger",
+"toggle-read is the trigger" — were timing coincidences; the verbs are sound.
+
+Operationally: docker mode stays mandatory (=telega-use-docker= = t; the setq
+before =(telega t)= is still the right defense), and *every action batch checks
+the server first* — =(process-live-p (telega-server--proc))= — restarting via
+=(telega t)= when dead and re-checking Ready before firing verbs. A mid-sweep
+death is recoverable, not an abort: restart, confirm Ready, resume. Durable-fix
+candidates if the crashing gets worse: pin a pre-2026-06 image digest, build
+=telega-server= natively against tdlib, or report upstream to zevlg with the
+coredumps (=coredumpctl list /usr/bin/telega-server=).
Defense in depth: even if the server does die, the scan still works because it
reads the cached =telega--chats= hash, not a live query. A dead server is
@@ -221,8 +231,29 @@ Actions need the tdlib server *live* (see the SEGFAULT gotcha — a dead server
scan-only). All run through telega in the daemon:
- reply :: =emacsclient -e "(telega-chat-send-msg (telega-chat-get <CHAT-ID>) \"<body>\")"= — public-facing (goes out under Craig's name), so run =/voice personal= before sending. Prefer a body file for multi-line.
-- mark-read :: =emacsclient -e "(telega-chat--mark-read (telega-chat-get <CHAT-ID>))"= — clears a specific chat's unread. Never mark the whole account read blindly; the join-notice noise is fine to leave or clear per Craig's call.
+- mark-read :: verified 2026-06-11 (the previously documented =telega-chat--mark-read= never existed in telega). The idempotent per-chat verb:
+
+ #+begin_example
+ emacsclient -e "(let ((chat (telega-chat-get <CHAT-ID>)))
+ (telega--viewMessages chat (list (plist-get chat :last_message))
+ :source '(:@type \"messageSourceChatList\") :force t)
+ (telega--readAllChatMentions chat)
+ (telega--readAllChatReactions chat))"
+ #+end_example
+
+ =telega-chat-toggle-read= also works but *toggles*: on a chat with zero unread it marks the chat UNREAD, so scripting must guard on =(> (plist-get chat :unread_count) 0)=. Never mark the whole account read blindly; a real DM is handled deliberately, not swept.
+- delete-join-notice :: standing policy (Craig, 2026-06-11): a chat whose *newest* message is a =messageContactRegistered= "joined Telegram" notice is a chat Craig never responded to and doesn't want to keep — *delete it* rather than mark it read. The bulk sweep (returns the count deleted):
+
+ #+begin_example
+ emacsclient -e "(let ((n 0))
+ (maphash (lambda (_id chat)
+ (when (equal (plist-get (plist-get (plist-get chat :last_message) :content) :@type)
+ \"messageContactRegistered\")
+ (telega--deleteChatHistory chat t nil)
+ (setq n (1+ n))))
+ telega--chats)
+ n)"
+ #+end_example
+
+ =telega--deleteChatHistory chat t nil= removes the chat from the list on Craig's side only (no revoke). First run 2026-06-11 deleted 41 such chats and cut the unread-chat count from 48 to 16.
- open :: =emacsclient -e "(telega-chat-with (telega-chat-get <CHAT-ID>))"= — pop the chat buffer for Craig to read/handle by hand (useful when a real DM needs a considered reply).
-
-No blanket mark-read verb: the account's unread is mostly join-notices and spam,
-and a real DM should be handled deliberately, not swept.
diff --git a/claude-templates/.ai/workflows/triage-intake.telegram.org b/claude-templates/.ai/workflows/triage-intake.telegram.org
index d855fcf..a318f12 100644
--- a/claude-templates/.ai/workflows/triage-intake.telegram.org
+++ b/claude-templates/.ai/workflows/triage-intake.telegram.org
@@ -157,14 +157,24 @@ stays non-nil). =telega-server-kill= is what actually stops the server. Call
left in =docker ps=. Skipping this whole branch when =TELEGA_WAS_RUNNING= is t is
the point of Step 0: never tear down a session Craig is actively using.
-⚠ *SEGFAULT GOTCHA — docker mode is the fix.* The tdlib =telega-server= can exit
-abnormally (code 139, SIGSEGV) during or just after the initial sync. Observed
-on the 2026-06-09 first run; the cure was *docker mode* — =telega-use-docker= = t
-with the =zevlg/telega-server:latest= image (already pulled on Craig's box).
-Restarted under docker, the server reached "Ready" in ~2s, synced 73 chats, and
-held through a live re-scan with no crash. So: *ensure docker mode is on before
-the sweep.* If =telega-use-docker= is nil, set it (=(setq telega-use-docker t)=)
-and restart telega.
+⚠ *SEGFAULT GOTCHA — crashes are spontaneous; treat server death as routine.*
+The dockerized =telega-server= (=zevlg/telega-server:latest=, image built
+2026-06-04, tdlib 1.8.64) SIGSEGVs (exit 139) *on its own*, minutes-to-hours
+into a session — 11 host coredumps between 2026-06-09 and 2026-06-11, several at
+times when no triage verb was running. The 2026-06-11 investigation reproduced
+the crash-free verbs and the spontaneous deaths side by side: coredump
+backtraces show a corrupted stack (memory corruption in the musl build), and
+no newer image exists upstream. Earlier theories — "native mode is the trigger",
+"toggle-read is the trigger" — were timing coincidences; the verbs are sound.
+
+Operationally: docker mode stays mandatory (=telega-use-docker= = t; the setq
+before =(telega t)= is still the right defense), and *every action batch checks
+the server first* — =(process-live-p (telega-server--proc))= — restarting via
+=(telega t)= when dead and re-checking Ready before firing verbs. A mid-sweep
+death is recoverable, not an abort: restart, confirm Ready, resume. Durable-fix
+candidates if the crashing gets worse: pin a pre-2026-06 image digest, build
+=telega-server= natively against tdlib, or report upstream to zevlg with the
+coredumps (=coredumpctl list /usr/bin/telega-server=).
Defense in depth: even if the server does die, the scan still works because it
reads the cached =telega--chats= hash, not a live query. A dead server is
@@ -221,8 +231,29 @@ Actions need the tdlib server *live* (see the SEGFAULT gotcha — a dead server
scan-only). All run through telega in the daemon:
- reply :: =emacsclient -e "(telega-chat-send-msg (telega-chat-get <CHAT-ID>) \"<body>\")"= — public-facing (goes out under Craig's name), so run =/voice personal= before sending. Prefer a body file for multi-line.
-- mark-read :: =emacsclient -e "(telega-chat--mark-read (telega-chat-get <CHAT-ID>))"= — clears a specific chat's unread. Never mark the whole account read blindly; the join-notice noise is fine to leave or clear per Craig's call.
+- mark-read :: verified 2026-06-11 (the previously documented =telega-chat--mark-read= never existed in telega). The idempotent per-chat verb:
+
+ #+begin_example
+ emacsclient -e "(let ((chat (telega-chat-get <CHAT-ID>)))
+ (telega--viewMessages chat (list (plist-get chat :last_message))
+ :source '(:@type \"messageSourceChatList\") :force t)
+ (telega--readAllChatMentions chat)
+ (telega--readAllChatReactions chat))"
+ #+end_example
+
+ =telega-chat-toggle-read= also works but *toggles*: on a chat with zero unread it marks the chat UNREAD, so scripting must guard on =(> (plist-get chat :unread_count) 0)=. Never mark the whole account read blindly; a real DM is handled deliberately, not swept.
+- delete-join-notice :: standing policy (Craig, 2026-06-11): a chat whose *newest* message is a =messageContactRegistered= "joined Telegram" notice is a chat Craig never responded to and doesn't want to keep — *delete it* rather than mark it read. The bulk sweep (returns the count deleted):
+
+ #+begin_example
+ emacsclient -e "(let ((n 0))
+ (maphash (lambda (_id chat)
+ (when (equal (plist-get (plist-get (plist-get chat :last_message) :content) :@type)
+ \"messageContactRegistered\")
+ (telega--deleteChatHistory chat t nil)
+ (setq n (1+ n))))
+ telega--chats)
+ n)"
+ #+end_example
+
+ =telega--deleteChatHistory chat t nil= removes the chat from the list on Craig's side only (no revoke). First run 2026-06-11 deleted 41 such chats and cut the unread-chat count from 48 to 16.
- open :: =emacsclient -e "(telega-chat-with (telega-chat-get <CHAT-ID>))"= — pop the chat buffer for Craig to read/handle by hand (useful when a real DM needs a considered reply).
-
-No blanket mark-read verb: the account's unread is mostly join-notices and spam,
-and a real DM should be handled deliberately, not swept.