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.personal-gmail.org | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .ai/workflows/triage-intake.personal-gmail.org (limited to '.ai/workflows/triage-intake.personal-gmail.org') diff --git a/.ai/workflows/triage-intake.personal-gmail.org b/.ai/workflows/triage-intake.personal-gmail.org new file mode 100644 index 0000000..aa0554d --- /dev/null +++ b/.ai/workflows/triage-intake.personal-gmail.org @@ -0,0 +1,53 @@ +#+TITLE: Triage Intake — Personal Gmail 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: personal-gmail +:PROPERTIES: +:ORDER: 20 +:ENABLED: mcp google-docs-personal present +:ANCHOR: epoch +:SUBAGENT_OVER: 50 +:END: + +** Scan + +Personal Gmail unread in the inbox since the anchor: + +#+begin_src text +mcp__google-docs-personal__listMessages q="is:unread in:inbox after:" maxResults=100 +#+end_src + +⚠ *Express the cutoff as the literal UNIX epoch* — =after:1778856990=, not =after:YYYY/MM/DD=. Gmail's =after:YYYY/MM/DD= operator only supports day resolution; the =YYYY/MM/DD HH:MM:SS= form is NOT valid syntax — Gmail parses the space as a term separator, treats =HH:MM:SS= as a search term that never matches, and returns 0 results, silently masking unread mail. The engine supplies == because this source declares =ANCHOR: epoch=. + +⚠ *Do NOT add =-category:promotions -category:social=.* That filter masked 67 promo+social messages across two runs (2026-05-04, 2026-05-06), both needing a follow-up sweep. Pull the full unfiltered set; the trash-leaning bias in Classify handles promotions and social directly. + +** Classify + +Bias: *trash-leaning* — personal Gmail is high noise volume. + +- *Noise-trash:* newsletters, Substacks, retail/SaaS marketing, social digests, redundant aggregator digests (Notion/Miro daily), wrong-recipient mail, past-event calendar artifacts. +- *Noise-keep:* receipts, order confirmations, statements — low value but worth the audit trail. +- *FYI:* substantive personal mail with no action owed. +- *Action:* an explicit ask, a reply owed, a time-sensitive personal matter. + +** Render + +#+begin_example +**Personal Gmail — N unread.** +- Action: +- FYI: +- Noise: N trash candidates, M keep +#+end_example + +Omit the block if zero unread. + +** Actions + +- trash :: =mcp__google-docs-personal__trashMessage= id= (recoverable from Gmail Trash for 30 days) +- mark-read :: =mcp__google-docs-personal__modifyMessageLabels= id= removeLabelIds=["UNREAD"] +- star+read :: =mcp__google-docs-personal__modifyMessageLabels= id= addLabelIds=["STARRED"] removeLabelIds=["UNREAD"] +- attach-fetch:: =.ai/scripts/gmail-fetch-attachments.py --profile personal --message-id --output-dir = -- cgit v1.2.3