operations-email-report
Three jobs, in this order:
Job 1 — Categorize: Search Gmail for unread + important emails from the last 24 hours. Categorize each one using references/content-patterns.md (matches by content fingerprint, not sender domain — survives tool swaps). Buckets: INBOUND / CLIENT / REVENUE / ADMIN / FYI / UNKNOWN.
Job 2 — Score: Apply references/email-rubric.md to assign a Priority Score (0-8) and badge (🔥 / 🔴 / 🟡 / ⚪). Score decides surfacing order; bucket decides which section.
Job 3 — Surface dropped balls: Separate query — find threads where you last replied >3 days ago and the other side hasn't responded. Surface in 🟠 Dropped Balls section.
Automation level: AUTONOMOUS — read-only. Nothing is sent, labeled, or modified in Gmail.
Routine position: Morning routine #1 — feeds operations-compile-daily-plan (skill #6).
Setup Required
Replace these placeholders in the context steps below with your actual Notion database IDs:
| Placeholder |
What it's for |
YOUR_CLIENT_DELIVERABLES_NOTION_DB_ID |
Your active clients database (powers the +3 sender bonus) |
YOUR_PEOPLE_CRM_NOTION_DB_ID |
Your CRM prospects database (powers the +2 sender bonus) |
Also update references/sender-aliases.md with your own contacts and references/noise-blocklist.md with your own noise domains.
Context to read before running
- Read
context/current-priorities.md — current priorities. An email tied to a current priority gets +1 to score (see rubric).
- Read
context/business-profile.md — known client/prospect company domains.
- Read
context/icp.md — for the form submission ICP mini-score.
- Read all four reference files in this skill:
references/email-rubric.md — scoring math
references/content-patterns.md — bucket-detection rules
references/sender-aliases.md — alias mapping
references/noise-blocklist.md — auto-FYI domains and self-suppress
- Query the Notion Client Deliverables data source (
collection://YOUR_CLIENT_DELIVERABLES_NOTION_DB_ID) — list of active clients (page IDs and names). Powers "Active client = +3" sender match.
- Query the Notion People data source (
collection://YOUR_PEOPLE_CRM_NOTION_DB_ID) — prospects in CRM (Status ≠ Lost / Cold). Powers "CRM prospect = +2" sender match.
Steps
Part 1 — Search Gmail
Three Gmail queries in parallel using mcp__claude_ai_Gmail__search_threads:
- Unread (24h):
query: "is:unread newer_than:1d", pageSize: 30
- Important unread (24h):
query: "is:important is:unread newer_than:1d", pageSize: 20
- Dropped balls (you last replied 3-14d ago):
query: "from:me older_than:3d newer_than:14d", pageSize: 30
Merge results from queries 1 + 2. Deduplicate by thread ID. Keep 30 most recent if total exceeds 30.
For each thread in the merged list, call mcp__claude_ai_Gmail__get_thread with messageFormat: FULL_CONTENT. Extract:
- sender name + sender email + sender domain
- subject
- first 200 chars of body
- thread length
- who sent the most recent message
- first sender in thread (to detect "you sent first → reply from prospect" override)
Part 2 — Pre-filter (suppress + blocklist)
- For each email, check
references/noise-blocklist.md:
- Self-suppress matches → drop entirely. Don't include in counts or report.
- Domain blocklist matches → force bucket = FYI, score = 0. Skip to Part 5.
Part 3 — Categorize by content pattern
For each remaining email, walk through references/content-patterns.md in this order:
- INBOUND patterns (1-5)
- CLIENT patterns (1-5)
- REVENUE patterns (1-5)
- ADMIN patterns (1-5)
- FYI patterns (1-6)
- If none match → bucket = UNKNOWN
First match wins. Record which pattern matched (used for action-item template).
Resolve sender aliases via references/sender-aliases.md before checking active-client / CRM lookup. (Same person across 2 emails should count once.)
Part 4 — Score with rubric
Apply references/email-rubric.md:
- Special-case overrides FIRST. If a content pattern matches a special override (inbound form = score 6, inbound booking = score 5, reply-to-outbound = min 3, time-bombed renewal = 3, SaaS-drip-pretending-to-be-human = 0, etc.) — apply it and skip the additive rubric.
- Otherwise, sum the 5 signals (sender match + subject + time-sensitivity + thread state + priority bonus). Cap at 8.
VIP check — for each non-suppressed, non-blocklisted email:
- Run a Gmail query:
from:<sender> newer_than:30d (cache results across the run — don't re-query for the same sender).
- Count distinct threads. If ≥ 3 AND domain is not bulk-mail (not
mail.*, email.*, notifications.*, noreply.*) → apply +3 sender match (overrides "unknown sender" 0).
Assign badge from final score: 🔥 ≥ 6 / 🔴 4–5 / 🟡 2–3 / ⚪ 0–1.
Part 5 — Detect dropped balls
- From the dropped-balls query (Part 1, query 3): for each thread:
- Read full thread.
- Check: did anyone reply AFTER your most recent message?
- If no → it's a dropped ball.
- Skip if your last message contained closure language: "ttyl", "no rush", "fyi", "thanks!", "no need to reply".
- For each true dropped ball, record: who, subject, days dormant, last action.
Part 6 — Action items
For each email scoring ≥ 2 in INBOUND, CLIENT, or REVENUE buckets, write one action item using the template from the matched content pattern.
- Format: "[Verb]: [topic] — from [Name]"
- Keep under 15 words.
- INBOUND form submissions get a parsed ICP mini-score appended: "ICP fit: HIGH/MEDIUM/LOW — [reason]"
- Call recap CLIENT emails get one action item per commitment extracted from the body.
⚪ emails (score 0–1) get no action item — informational only.
Add one action item per dropped ball: "Followup: [subject] — [Name] (X days dormant)"
Part 7 — Build and save output
Sort each bucket internally by score descending.
Save to inbox/outputs/md/YYYY-MM-DD-operations-email-report.md. If a file for today already exists, append with a ### Check — HH:MM header.
Print short summary inline (not the full report):
- "Email report done: X INBOUND, X CLIENT (Y action), X REVENUE (Y action), X ADMIN, X FYI, X UNKNOWN, X dropped balls. Top score: [score] — [subject]."
If UNKNOWN section has entries, also print: "X emails couldn't be categorized — check the UNKNOWN section in the report and tell me what they are so I can add a rule to references/content-patterns.md."
Output Format
# Email Report — YYYY-MM-DD HH:MM
> X total scanned. INBOUND X, CLIENT X, REVENUE X, ADMIN X, FYI X, UNKNOWN X. Dropped balls: X. Action items: Y.
---
## Priority rubric (recap)
- 🔥 ≥6 / 🔴 4–5 / 🟡 2–3 / ⚪ 0–1 / 🟠 Dropped ball (separate)
- Active client or VIP (3+ threads/30d): +3 / CRM prospect: +2 / Known company: +1
- Subject "proposal/invoice/blocked/urgent": +2 / "pricing/call/follow-up": +1
- Body "today/EOD/ASAP": +2 / "this week": +1
- You holding the ball: +2 / You last replied: -1
- Priority bonus: +1
- Special overrides (form = 6, booking = 5, reply-to-outbound = min 3, blocklist = 0)
---
## 🆕 INBOUND (X)
| Score | From | Subject | Pattern matched | Action |
|-------|------|---------|-----------------|--------|
| 🔥 6 | [Contact Name] | Free Audit Call | Tally form submission | Reply within 24h. ICP fit: HIGH — [reason] |
| 🔴 5 | [Contact Name] | New booking | Cal.com | Meeting booked: [Name] on YYYY-MM-DD — run meeting prep 24h before. |
---
## 👥 CLIENT (X)
| Score | From | Subject | Preview | Action |
|-------|------|---------|---------|--------|
| 🔥 7 | [Client Name] | Re: project update | "When can I expect..." | Reply: ETA update — from [Client] |
---
## 💰 REVENUE (X)
| Score | From | Subject | Preview | Action |
|-------|------|---------|---------|--------|
| 🔴 4 | [Prospect Name] | Re: proposal | "...may still need your help..." | Warm reply — review thread, draft response |
---
## 🟠 Dropped Balls (X)
| Days dormant | Who | Subject | Last action |
|--------------|-----|---------|-------------|
| 5 | [Name] | [Subject] | You sent proposal v1, no response |
---
## ⚙️ ADMIN (X)
| Score | From | Subject | Preview | Note |
|-------|------|---------|---------|------|
| 🟡 3 | Excalidraw | Subscription renews | "..." | Time-bombed: 3 days. Keep or cancel? |
---
## 📌 FYI (X)
| Score | From | Subject | Note |
|-------|------|---------|------|
| ⚪ 0 | Apollo | [Subject] | Blocklist |
---
## ❓ UNKNOWN (X) — review and tell me how to categorize
| From | Subject | Why unclassified |
|------|---------|------------------|
| [Sender] | [Subject] | Not in CRM, no known pattern matched |
---
## Action Items for Today (top-scored only)
- [ ] 🔥 Reply within 24h: [Name] submission (HIGH ICP fit)
- [ ] 🔴 Meeting booked: [Name] on [Date] — run meeting prep 24h before
- [ ] 🟠 Followup: [proposal subject] — [Name] (5 days dormant)
Edge Cases
- Inbox empty / no unread emails: Output "Inbox clear — no unread emails in last 24h." Pass forward to daily plan.
- Gmail API fails or times out: Log "Gmail unavailable — email report skipped." Continue morning routine.
- Notion query for active clients fails: Skip the +3 sender bonus from active clients (VIP detection still works as fallback).
- Can't read full email body: Score sender + subject only. Time-sensitivity and thread state default to 0.
- Reference file missing or empty: Treat all emails as UNKNOWN (don't crash). Print "Reference file
[name] is empty — fill it before next run."
- More than 30 emails in 24h: Process the 30 most recent. Note "X additional unread not scored — check inbox directly."
- Two emails from the same thread: Score both, but only generate one action item (use the most recent message).
- UNKNOWN bucket has more than 5: Show 5 most recent. Note "X more unclassified — review in inbox."
- VIP query times out: Skip VIP detection for that sender, fall back to standard scoring.
- Form parsing fails: Surface the email as INBOUND with action "Form received — couldn't parse fields, check inbox."
What good looks like
A finished email report is a decision aid, not a copy of the inbox. After reading it, you know in <60 seconds:
- Which 1-3 emails to reply to right now (🔥 / 🔴 in INBOUND, CLIENT, REVENUE).
- Which meetings need prep this week (INBOUND bookings).
- Which threads you've dropped the ball on (🟠).
- Which subscriptions are about to auto-renew (ADMIN time-bombed).
- Whether anything new appeared that the system doesn't recognize (UNKNOWN).
The UNKNOWN section is the feedback loop — every entry there is a chance to make the skill smarter for next run.
1---2name: operations-email-report3description: Scans Gmail for the last 24 hours of emails, categorizes each by content pattern (INBOUND / CLIENT / REVENUE / ADMIN / FYI / UNKNOWN), scores priority, surfaces dropped-ball threads, and outputs action items. Use this skill when the user says "check my email", "email report", "what emails came in", "scan my inbox", or "morning emails". Also trigger at position4---56# operations-email-report78Three jobs, in this order:910**Job 1 — Categorize:** Search Gmail for unread + important emails from the last 24 hours. Categorize each one using `references/content-patterns.md` (matches by content fingerprint, not sender domain — survives tool swaps). Buckets: INBOUND / CLIENT / REVENUE / ADMIN / FYI / UNKNOWN.1112**Job 2 — Score:** Apply `references/email-rubric.md` to assign a Priority Score (0-8) and badge (🔥 / 🔴 / 🟡 / ⚪). Score decides surfacing order; bucket decides which section.1314**Job 3 — Surface dropped balls:** Separate query — find threads where you last replied >3 days ago and the other side hasn't responded. Surface in 🟠 Dropped Balls section.1516**Automation level:** AUTONOMOUS — read-only. Nothing is sent, labeled, or modified in Gmail.1718**Routine position:** Morning routine #1 — feeds operations-compile-daily-plan (skill #6).1920---2122## Setup Required2324Replace these placeholders in the context steps below with your actual Notion database IDs:2526| Placeholder | What it's for |27|-------------|--------------|28| `YOUR_CLIENT_DELIVERABLES_NOTION_DB_ID` | Your active clients database (powers the +3 sender bonus) |29| `YOUR_PEOPLE_CRM_NOTION_DB_ID` | Your CRM prospects database (powers the +2 sender bonus) |3031Also update `references/sender-aliases.md` with your own contacts and `references/noise-blocklist.md` with your own noise domains.3233---3435## Context to read before running36371. Read `context/current-priorities.md` — current priorities. An email tied to a current priority gets +1 to score (see rubric).382. Read `context/business-profile.md` — known client/prospect company domains.393. Read `context/icp.md` — for the form submission ICP mini-score.404. Read **all four reference files** in this skill:41 - `references/email-rubric.md` — scoring math42 - `references/content-patterns.md` — bucket-detection rules43 - `references/sender-aliases.md` — alias mapping44 - `references/noise-blocklist.md` — auto-FYI domains and self-suppress455. Query the Notion **Client Deliverables** data source (`collection://YOUR_CLIENT_DELIVERABLES_NOTION_DB_ID`) — list of active clients (page IDs and names). Powers "Active client = +3" sender match.466. Query the Notion **People** data source (`collection://YOUR_PEOPLE_CRM_NOTION_DB_ID`) — prospects in CRM (Status ≠ Lost / Cold). Powers "CRM prospect = +2" sender match.4748---4950## Steps5152### Part 1 — Search Gmail53541. Three Gmail queries in parallel using `mcp__claude_ai_Gmail__search_threads`:55 - **Unread (24h):** `query: "is:unread newer_than:1d"`, pageSize: 3056 - **Important unread (24h):** `query: "is:important is:unread newer_than:1d"`, pageSize: 2057 - **Dropped balls (you last replied 3-14d ago):** `query: "from:me older_than:3d newer_than:14d"`, pageSize: 3058592. Merge results from queries 1 + 2. Deduplicate by thread ID. Keep 30 most recent if total exceeds 30.60613. For each thread in the merged list, call `mcp__claude_ai_Gmail__get_thread` with `messageFormat: FULL_CONTENT`. Extract:62 - sender name + sender email + sender domain63 - subject64 - first 200 chars of body65 - thread length66 - who sent the most recent message67 - first sender in thread (to detect "you sent first → reply from prospect" override)6869### Part 2 — Pre-filter (suppress + blocklist)70714. For each email, check `references/noise-blocklist.md`:72 - **Self-suppress matches** → drop entirely. Don't include in counts or report.73 - **Domain blocklist matches** → force bucket = FYI, score = 0. Skip to Part 5.7475### Part 3 — Categorize by content pattern76775. For each remaining email, walk through `references/content-patterns.md` in this order:78 - INBOUND patterns (1-5)79 - CLIENT patterns (1-5)80 - REVENUE patterns (1-5)81 - ADMIN patterns (1-5)82 - FYI patterns (1-6)83 - If none match → bucket = UNKNOWN8485 First match wins. Record which pattern matched (used for action-item template).86876. Resolve sender aliases via `references/sender-aliases.md` before checking active-client / CRM lookup. (Same person across 2 emails should count once.)8889### Part 4 — Score with rubric90917. Apply `references/email-rubric.md`:92 - **Special-case overrides FIRST.** If a content pattern matches a special override (inbound form = score 6, inbound booking = score 5, reply-to-outbound = min 3, time-bombed renewal = 3, SaaS-drip-pretending-to-be-human = 0, etc.) — apply it and skip the additive rubric.93 - Otherwise, sum the 5 signals (sender match + subject + time-sensitivity + thread state + priority bonus). Cap at 8.94958. **VIP check** — for each non-suppressed, non-blocklisted email:96 - Run a Gmail query: `from:<sender> newer_than:30d` (cache results across the run — don't re-query for the same sender).97 - Count distinct threads. If ≥ 3 AND domain is not bulk-mail (not `mail.*`, `email.*`, `notifications.*`, `noreply.*`) → apply +3 sender match (overrides "unknown sender" 0).98999. Assign badge from final score: 🔥 ≥ 6 / 🔴 4–5 / 🟡 2–3 / ⚪ 0–1.100101### Part 5 — Detect dropped balls10210310. From the dropped-balls query (Part 1, query 3): for each thread:104 - Read full thread.105 - Check: did anyone reply AFTER your most recent message?106 - If no → it's a dropped ball.107 - Skip if your last message contained closure language: "ttyl", "no rush", "fyi", "thanks!", "no need to reply".108 - For each true dropped ball, record: who, subject, days dormant, last action.109110### Part 6 — Action items11111211. For each email scoring ≥ 2 in INBOUND, CLIENT, or REVENUE buckets, write one action item using the template from the matched content pattern.113 - Format: "[Verb]: [topic] — from [Name]"114 - Keep under 15 words.115 - INBOUND form submissions get a parsed ICP mini-score appended: "ICP fit: HIGH/MEDIUM/LOW — [reason]"116 - Call recap CLIENT emails get one action item per commitment extracted from the body.11711812. ⚪ emails (score 0–1) get no action item — informational only.11912013. Add one action item per dropped ball: "Followup: [subject] — [Name] (X days dormant)"121122### Part 7 — Build and save output12312414. Sort each bucket internally by score descending.12512615. Save to `inbox/outputs/md/YYYY-MM-DD-operations-email-report.md`. If a file for today already exists, append with a `### Check — HH:MM` header.12712816. Print short summary inline (not the full report):129 - "Email report done: X INBOUND, X CLIENT (Y action), X REVENUE (Y action), X ADMIN, X FYI, X UNKNOWN, X dropped balls. Top score: [score] — [subject]."13013117. If UNKNOWN section has entries, also print: "X emails couldn't be categorized — check the UNKNOWN section in the report and tell me what they are so I can add a rule to `references/content-patterns.md`."132133---134135## Output Format136137```138# Email Report — YYYY-MM-DD HH:MM139140> X total scanned. INBOUND X, CLIENT X, REVENUE X, ADMIN X, FYI X, UNKNOWN X. Dropped balls: X. Action items: Y.141142---143144## Priority rubric (recap)145- 🔥 ≥6 / 🔴 4–5 / 🟡 2–3 / ⚪ 0–1 / 🟠 Dropped ball (separate)146- Active client or VIP (3+ threads/30d): +3 / CRM prospect: +2 / Known company: +1147- Subject "proposal/invoice/blocked/urgent": +2 / "pricing/call/follow-up": +1148- Body "today/EOD/ASAP": +2 / "this week": +1149- You holding the ball: +2 / You last replied: -1150- Priority bonus: +1151- Special overrides (form = 6, booking = 5, reply-to-outbound = min 3, blocklist = 0)152153---154155## 🆕 INBOUND (X)156| Score | From | Subject | Pattern matched | Action |157|-------|------|---------|-----------------|--------|158| 🔥 6 | [Contact Name] | Free Audit Call | Tally form submission | Reply within 24h. ICP fit: HIGH — [reason] |159| 🔴 5 | [Contact Name] | New booking | Cal.com | Meeting booked: [Name] on YYYY-MM-DD — run meeting prep 24h before. |160161---162163## 👥 CLIENT (X)164| Score | From | Subject | Preview | Action |165|-------|------|---------|---------|--------|166| 🔥 7 | [Client Name] | Re: project update | "When can I expect..." | Reply: ETA update — from [Client] |167168---169170## 💰 REVENUE (X)171| Score | From | Subject | Preview | Action |172|-------|------|---------|---------|--------|173| 🔴 4 | [Prospect Name] | Re: proposal | "...may still need your help..." | Warm reply — review thread, draft response |174175---176177## 🟠 Dropped Balls (X)178| Days dormant | Who | Subject | Last action |179|--------------|-----|---------|-------------|180| 5 | [Name] | [Subject] | You sent proposal v1, no response |181182---183184## ⚙️ ADMIN (X)185| Score | From | Subject | Preview | Note |186|-------|------|---------|---------|------|187| 🟡 3 | Excalidraw | Subscription renews | "..." | Time-bombed: 3 days. Keep or cancel? |188189---190191## 📌 FYI (X)192| Score | From | Subject | Note |193|-------|------|---------|------|194| ⚪ 0 | Apollo | [Subject] | Blocklist |195196---197198## ❓ UNKNOWN (X) — review and tell me how to categorize199| From | Subject | Why unclassified |200|------|---------|------------------|201| [Sender] | [Subject] | Not in CRM, no known pattern matched |202203---204205## Action Items for Today (top-scored only)206- [ ] 🔥 Reply within 24h: [Name] submission (HIGH ICP fit)207- [ ] 🔴 Meeting booked: [Name] on [Date] — run meeting prep 24h before208- [ ] 🟠 Followup: [proposal subject] — [Name] (5 days dormant)209```210211---212213## Edge Cases214215- **Inbox empty / no unread emails:** Output "Inbox clear — no unread emails in last 24h." Pass forward to daily plan.216- **Gmail API fails or times out:** Log "Gmail unavailable — email report skipped." Continue morning routine.217- **Notion query for active clients fails:** Skip the +3 sender bonus from active clients (VIP detection still works as fallback).218- **Can't read full email body:** Score sender + subject only. Time-sensitivity and thread state default to 0.219- **Reference file missing or empty:** Treat all emails as UNKNOWN (don't crash). Print "Reference file `[name]` is empty — fill it before next run."220- **More than 30 emails in 24h:** Process the 30 most recent. Note "X additional unread not scored — check inbox directly."221- **Two emails from the same thread:** Score both, but only generate one action item (use the most recent message).222- **UNKNOWN bucket has more than 5:** Show 5 most recent. Note "X more unclassified — review in inbox."223- **VIP query times out:** Skip VIP detection for that sender, fall back to standard scoring.224- **Form parsing fails:** Surface the email as INBOUND with action "Form received — couldn't parse fields, check inbox."225226---227228## What good looks like229230A finished email report is a **decision aid**, not a copy of the inbox. After reading it, you know in <60 seconds:2312321. Which 1-3 emails to reply to right now (🔥 / 🔴 in INBOUND, CLIENT, REVENUE).2332. Which meetings need prep this week (INBOUND bookings).2343. Which threads you've dropped the ball on (🟠).2354. Which subscriptions are about to auto-renew (ADMIN time-bombed).2365. Whether anything new appeared that the system doesn't recognize (UNKNOWN).237238The UNKNOWN section is the feedback loop — every entry there is a chance to make the skill smarter for next run.