aboutsummaryrefslogtreecommitdiff
path: root/docs/workflows/summarize-emails.org
blob: a8ab8051afeadd569e123a0d6f1b5bc778a0c8cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#+TITLE: Summarize Emails Workflow
#+AUTHOR: Craig Jennings & Claude
#+DATE: 2026-02-14

* Overview

This workflow filters out marketing noise and surfaces only emails that matter — messages from real people, businesses Craig works with, or anything that needs his attention. It chains together existing tools: sync-email, mu queries, the extract script, and Claude's judgment to produce a curated summary.

* Problem We're Solving

Craig's inbox contains a mix of important correspondence and marketing noise. Manually scanning through emails to find what matters wastes time and risks missing something important buried under promotional messages. This workflow automates the filtering and presents a concise summary of only the emails that deserve attention.

* Exit Criteria

Summary is complete when:
1. All qualifying emails in the requested scope have been reviewed
2. Summary presented to Craig, grouped by account
3. Temp directory cleaned up
4. Session context file updated

* When to Use This Workflow

When Craig says:
- "summarize my emails", "what emails do I have", "anything important in my inbox", "email summary"
- "any unread emails", "check my unread"
- "any starred emails", "show flagged emails"
- "emails from [person]", "what has [person] sent me"
- "emails also sent to Christine"

* The Workflow

** Step 1: Context Hygiene

Before starting, write out the session context file and ask Craig if he wants to compact first. This workflow is token-heavy (reading multiple full emails). If the context window compresses mid-workflow, we may lose important details. Writing out session context prevents this data loss.

** Step 2: Parse Scope

Determine the mu query from Craig's request. Supported scope types:

| Scope Type         | Example Request                | mu Query                              |
|--------------------+--------------------------------+---------------------------------------|
| Date range         | "last week", "since Monday"    | =date:1w..now=, =date:2026-02-10..now= |
| Unread only        | "unread emails"                | =flag:unread=                           |
| Unread + date      | "unread emails this week"      | =flag:unread date:1w..now=              |
| Starred/flagged    | "starred emails"               | =flag:flagged= (with optional date)     |
| From a sender      | "emails from Dan"              | =from:dan= (with optional =--maxnum=)    |
| Sent to someone    | "emails also sent to Christine"| =to:christine OR cc:christine=          |

Scopes can be combined. For example, "unread emails from Dan this week" becomes =flag:unread from:dan date:1w..now=.

If no scope is provided or it's ambiguous, *ask Craig* before querying.

** Step 3: Offer to Sync

Ask Craig if he wants to sync first (=mbsync -a && mu index=). Don't auto-sync. If Craig confirms, run the [[file:sync-email.org][sync-email workflow]].

** Step 4: Query mu

Append =NOT flag:list= to the query to exclude emails with List-* headers (catches most mailing list / marketing / bulk mail).

#+begin_src bash
mu find --sortfield=date --reverse --fields="d f t s l" [query] NOT flag:list
#+end_src

Output fields: date, from, to, subject, path. Sorted by date, newest first.

** Step 5: Copy Qualifying Emails to Temp Directory

Create an isolated temp directory for the summary work:

#+begin_src bash
mkdir -p ./tmp/email-summary-YYYY-MM-DD/
#+end_src

Copy the EML files from their maildir paths into this directory.

*CRITICAL: Copy FROM =~/.mail/=, never modify =~/.mail/=.*

#+begin_src bash
cp ~/.mail/gmail/INBOX/cur/message.eml ./tmp/email-summary-YYYY-MM-DD/
#+end_src

** Step 6: Second-Pass Header Inspection

For each copied email, check headers for additional marketing signals that mu's =flag:list= might miss. Discard emails that match any of:

*** Bulk Sender Tools
=X-Mailer= or =X-Mailtool= containing: Mailchimp, ExactTarget, Salesforce, SendGrid, Constant Contact, Campaign Monitor, HubSpot, Marketo, Brevo, Klaviyo

*** Bulk Precedence
=Precedence: bulk= or =Precedence: list=

*** Bulk Sender Patterns
=X-PM-Message-Id= patterns typical of bulk senders

*** Marketing From Addresses
From address matching: =noreply@=, =no-reply@=, =newsletter@=, =marketing@=, =promotions@=

** Step 7: Verify Addressed to Craig

Check To/CC headers contain one of Craig's addresses:
- =craigmartinjennings@gmail.com=
- =c@cjennings.net=

Discard BCC-only marketing blasts where Craig isn't in To/CC.

** Step 8: Run Extract Script on Survivors

Use =eml-view-and-extract-attachments.py= in stdout mode (no =--output-dir=) to read each email's content:

#+begin_src bash
python3 docs/scripts/eml-view-and-extract-attachments.py ./tmp/email-summary-YYYY-MM-DD/message.eml
#+end_src

This prints headers and body text to stdout without creating any files in the project.

** Step 9: Triage and Summarize

For each email, apply judgment:
- *Clearly needs Craig's attention* → summarize it (who, what, any action needed)
- *Unsure whether important* → summarize it with a note about why it might matter
- *Clearly unimportant* (automated notifications, receipts for known purchases, etc.) → mention it briefly but don't summarize in detail

** Step 10: Present Summary

Group by account (gmail / cmail). For each email show:
- From, Subject, Date
- Brief summary of content and any action needed
- Flag anything time-sensitive

Example output format:

#+begin_example
** Gmail

1. From: Dan Smith | Subject: Project update | Date: Feb 14
   Dan is asking about the timeline for the next milestone. Needs a reply.

2. From: Dr. Lee's Office | Subject: Appointment confirmation | Date: Feb 13
   Appointment confirmed for Feb 20 at 2pm. No action needed.

** cmail

1. From: Christine | Subject: Weekend plans | Date: Feb 14
   Asking about Saturday dinner. Needs a reply.

** Skipped (not important)
- Order confirmation from Amazon (Feb 13)
- GitHub notification: CI passed (Feb 14)
#+end_example

** Step 11: Clean Up

Remove the temp directory:

#+begin_src bash
rm -rf ./tmp/email-summary-YYYY-MM-DD/
#+end_src

If =./tmp/= is now empty, remove it too.

** Step 12: Post-Summary Actions

After presenting the summary, ask Craig if he wants to:

*** Star emails

Star specific emails by passing their maildir paths:

#+begin_src bash
python3 docs/scripts/maildir-flag-manager.py star --reindex /path/to/message1 /path/to/message2
#+end_src

To also mark starred emails as read in one step:

#+begin_src bash
python3 docs/scripts/maildir-flag-manager.py star --mark-read --reindex /path/to/message1
#+end_src

*** Mark reviewed emails as read

Mark all unread INBOX emails as read across both accounts:

#+begin_src bash
python3 docs/scripts/maildir-flag-manager.py mark-read --reindex
#+end_src

Or mark specific emails as read:

#+begin_src bash
python3 docs/scripts/maildir-flag-manager.py mark-read --reindex /path/to/message1 /path/to/message2
#+end_src

Use =--dry-run= to preview what would change without modifying anything.

The script uses atomic =os.rename()= directly on maildir files — the same mechanism mu4e uses. Flag changes are persisted to the filesystem so mbsync picks them up on the next sync.

*** Delete emails (future)
=mu= supports =mu remove= to delete messages from the filesystem and database. Not yet integrated into this workflow — explore when ready.

** Step 13: Context Hygiene (Completion)

Write out session-context.org again after the summary is presented, capturing what was reviewed and any action items identified.

* Principles

- *=maildir-flag-manager.py= for flag changes* — use the script for mark-read and star operations; it uses atomic =os.rename()= on maildir files (same mechanism as mu4e) and mbsync syncs changes on next run
- *Ask before syncing* — don't auto-sync; Craig may have already synced or may not want to wait
- *Ask before querying* — if scope is ambiguous, clarify rather than guess
- *Filter aggressively, surface generously* — when in doubt about whether an email is marketing, filter it out; when in doubt about whether it's important, include it in the summary
- *One pass through the extract script* — don't re-read emails; read once and summarize
- *Stdout mode only* — use the extract script without =--output-dir= to avoid creating files in the project
- *Clean up always* — remove the temp directory even if errors occur partway through

* Tools Reference

| Tool                                | Purpose                              |
|-------------------------------------+--------------------------------------|
| mbsync / mu index                   | Sync and index mail                  |
| mu find                             | Query maildir for matching emails    |
| eml-view-and-extract-attachments.py | Read email content (stdout mode)     |
| maildir-flag-manager.py             | Mark read, star (batch flag changes) |

* Files Referenced

| File                                           | Purpose                 |
|------------------------------------------------+-------------------------|
| [[file:sync-email.org][docs/workflows/sync-email.org]]                  | Sync step               |
| [[file:find-email.org][docs/workflows/find-email.org]]                  | mu query patterns       |
| docs/scripts/eml-view-and-extract-attachments.py | Extract script          |
| docs/scripts/maildir-flag-manager.py             | Flag management script  |
| =~/.mail/gmail/=                                 | Gmail maildir (READ ONLY) |
| =~/.mail/cmail/=                                 | cmail maildir (READ ONLY) |

* Living Document

Update this workflow as we discover new marketing patterns to filter, useful query combinations, or improvements to the summary format.