From f9c72c817290bb5433e593b2a8d1cfaa25431d20 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 26 May 2026 01:57:48 -0500 Subject: refactor(workflows): split triage-intake into engine + source plugins The triage-intake workflow had every source baked into one file, so adding or changing a source meant editing the workflow itself. I replaced it with a source-agnostic engine plus per-source plugins named triage-intake..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, and action knowledge moved into its own plugin. Four general plugins ship in the template: personal-gmail, personal-calendar, cmail, and github-prs. Project-specific sources live in the project's .ai/project-workflows/ and are never synced. Phase 0 globs both directories so a project source can't silently drop out of the sweep. I taught INDEX.org and the startup workflow-discovery drift check the namespace. A file matching .*.org is a plugin of that engine, not an orphan, and gets no trigger entry of its own. A "run the triage-intake workflow" request routes to the engine, never to a plugin. --- .ai/workflows/triage-intake.cmail.org | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .ai/workflows/triage-intake.cmail.org (limited to '.ai/workflows/triage-intake.cmail.org') diff --git a/.ai/workflows/triage-intake.cmail.org b/.ai/workflows/triage-intake.cmail.org new file mode 100644 index 0000000..d818c72 --- /dev/null +++ b/.ai/workflows/triage-intake.cmail.org @@ -0,0 +1,53 @@ +#+TITLE: Triage Intake — cmail (Proton) Source +#+AUTHOR: Craig Jennings & Claude +#+DATE: 2026-05-26 + +# 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. + +* Source: cmail +:PROPERTIES: +:ORDER: 25 +:ENABLED: test -f .ai/scripts/cmail-action.py +:ANCHOR: none +:SUBAGENT_OVER: 50 +:END: + +** Scan + +Proton (=c@cjennings.net=) via the bridge script. =ANCHOR: none= because this reports live IMAP unread *state*, not a since-window — the engine substitutes no cutoff. Phase B uses the anchor only to flag which of the current unread arrived since last check. + +#+begin_src bash +python3 .ai/scripts/cmail-action.py list-unread +#+end_src + +JSON output, keyed by UID. The script ignores messages already flagged =\Deleted= (those are pending-flush on the next Proton sync), so the list is the genuinely-live unread set. + +** Classify + +Bias: *trash-leaning*, like personal Gmail — cmail catches a lot of forwarded noise. + +- *Noise-trash:* newsletters, marketing, social, automated alerts. +- *Noise-keep:* receipts, statements. +- *FYI:* substantive mail, no action owed. +- *Action:* an explicit ask or reply owed. Flag "new since last check" by comparing the message date against the engine's anchor. + +** Render + +#+begin_example +**cmail (Proton) — N unread.** +- Action: +- FYI: +- Noise: N trash candidates, M keep +#+end_example + +Omit if zero unread. + +** Actions + +All take one or more UIDs (from the =list-unread= JSON): + +- mark-read :: =python3 .ai/scripts/cmail-action.py mark-read = +- star :: =python3 .ai/scripts/cmail-action.py star = +- unstar :: =python3 .ai/scripts/cmail-action.py unstar = +- trash :: =python3 .ai/scripts/cmail-action.py trash = (flags =\Deleted=; flushed on next Proton sync) -- cgit v1.2.3