Spec: outbound email follow-up tracker
This commit is contained in:
parent
90485d381d
commit
6d48e6a826
|
|
@ -56,4 +56,4 @@ triage:
|
|||
base_url: https://api.fireworks.ai/inference/v1
|
||||
api_key: ${FIREWORKS_API_KEY}
|
||||
model: accounts/fireworks/models/kimi-k2p5
|
||||
dashboard_url: http://localhost:9202/api/alerts
|
||||
dashboard_url: http://localhost:9200/api/news
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
# Outbound Email Follow-Up Tracker
|
||||
|
||||
*Spec — February 14, 2026*
|
||||
*Status: Parked, ready to build*
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
Johan sends emails that require responses. If no reply comes, nobody notices until he manually remembers to check. Conversations also move to phone/WhatsApp/in-person — so "no email reply" doesn't always mean "not handled."
|
||||
|
||||
## Solution
|
||||
|
||||
Triage outgoing emails the same way we triage incoming: K2.5 reads each sent email and decides if it expects a reply. If yes, track it with a deadline. If the deadline passes with no reply, push one alert to Fully. Dismiss via Fully or auto-resolve on inbound reply.
|
||||
|
||||
## Architecture
|
||||
|
||||
Everything lives in MC. Fully is just the display layer.
|
||||
|
||||
### IMAP Changes
|
||||
|
||||
Add Sent folder polling to existing connectors:
|
||||
- `tj_jongsma_me` → poll `Sent` alongside `INBOX`
|
||||
- `johan_jongsma_me` → poll `Sent` alongside `INBOX`
|
||||
|
||||
Tag outgoing messages with `direction: "outbound"` in MC's message store (inbound messages get `direction: "inbound"` or remain untagged for backwards compat).
|
||||
|
||||
### Schema: `follow_ups` table (orchestration.db)
|
||||
|
||||
```sql
|
||||
CREATE TABLE follow_ups (
|
||||
id TEXT PRIMARY KEY, -- MC message ID of the outbound email
|
||||
to_address TEXT NOT NULL, -- recipient email
|
||||
to_name TEXT, -- recipient display name
|
||||
subject TEXT, -- email subject
|
||||
sent_at TEXT NOT NULL, -- ISO timestamp
|
||||
deadline_hours INTEGER DEFAULT 48, -- expected response window
|
||||
expects_reply INTEGER DEFAULT 1, -- 1=yes, 0=no (triage decided no)
|
||||
reply_received INTEGER DEFAULT 0, -- auto-set on inbound match
|
||||
reply_message_id TEXT, -- MC message ID of the inbound reply
|
||||
alerted_at TEXT, -- timestamp when Fully alert was pushed (NULL = not yet)
|
||||
dismissed INTEGER DEFAULT 0, -- set via Fully callback
|
||||
dismissed_at TEXT, -- timestamp
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
```
|
||||
|
||||
### Triage (K2.5)
|
||||
|
||||
Same triage engine, second pass for outbound messages. Prompt addition:
|
||||
|
||||
```
|
||||
For OUTBOUND emails, determine:
|
||||
1. Does this email expect a reply? (question, request, proposal, scheduling = yes. FYI, acknowledgment, thank you = no)
|
||||
2. How urgent is the expected reply?
|
||||
- urgent: 24h (time-sensitive, medical, financial)
|
||||
- normal: 48h (business correspondence, requests)
|
||||
- low: 7d (informational, low-priority asks)
|
||||
3. Output: { "expects_reply": true/false, "deadline_hours": 24|48|168 }
|
||||
```
|
||||
|
||||
### Alert Flow
|
||||
|
||||
**Check runs every hour (cron or heartbeat):**
|
||||
|
||||
```
|
||||
SELECT * FROM follow_ups
|
||||
WHERE expects_reply = 1
|
||||
AND reply_received = 0
|
||||
AND dismissed = 0
|
||||
AND alerted_at IS NULL
|
||||
AND datetime(sent_at, '+' || deadline_hours || ' hours') < datetime('now')
|
||||
```
|
||||
|
||||
For each result:
|
||||
1. POST to Fully: `{ "message": "🟠 No reply from {to_name} re: {subject} — sent {N} days ago", "priority": "warning" }`
|
||||
2. SET `alerted_at = now()`
|
||||
3. **Never alert again for this follow-up**
|
||||
|
||||
### Auto-Resolve
|
||||
|
||||
When an inbound email arrives, check if sender matches any active follow-up:
|
||||
|
||||
```
|
||||
UPDATE follow_ups
|
||||
SET reply_received = 1, reply_message_id = ?
|
||||
WHERE to_address = ?
|
||||
AND reply_received = 0
|
||||
AND dismissed = 0
|
||||
ORDER BY sent_at DESC
|
||||
LIMIT 1
|
||||
```
|
||||
|
||||
Match by email address. Thread matching (In-Reply-To header) is better but address matching covers 90% of cases and is simpler.
|
||||
|
||||
### Fully Dismiss Callback
|
||||
|
||||
When a follow-up alert is dismissed on Fully (long-press done or ×):
|
||||
|
||||
Alert dashboard POSTs to MC:
|
||||
```
|
||||
POST /api/follow-ups/{id}/dismiss
|
||||
```
|
||||
|
||||
MC sets `dismissed = 1, dismissed_at = now()`.
|
||||
|
||||
This requires:
|
||||
1. Follow-up alerts include the MC follow-up ID in their metadata
|
||||
2. Alert dashboard `removeAlert` / done handler checks for follow-up ID and calls MC
|
||||
|
||||
### Auto-Expire
|
||||
|
||||
Follow-ups that were alerted but not dismissed auto-expire after 14 days:
|
||||
|
||||
```
|
||||
UPDATE follow_ups SET dismissed = 1
|
||||
WHERE alerted_at IS NOT NULL
|
||||
AND dismissed = 0
|
||||
AND datetime(alerted_at, '+14 days') < datetime('now')
|
||||
```
|
||||
|
||||
Runs on the same hourly check.
|
||||
|
||||
## Edge Cases
|
||||
|
||||
- **Reply via WhatsApp/phone:** Johan dismisses the Fully alert. That's the signal.
|
||||
- **Multiple emails to same person:** Each gets its own follow-up. Inbound reply resolves the most recent one.
|
||||
- **CC/BCC recipients:** Only track the TO address. CC'd people rarely owe a reply.
|
||||
- **Auto-replies/OOO:** Don't count as a real reply. Triage can detect these.
|
||||
- **Johan replies again (bump):** If Johan sends a second email to the same person on the same thread, reset the deadline on the existing follow-up.
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. Add Sent folder to IMAP connectors
|
||||
2. Add `direction` field to MC message model
|
||||
3. Create `follow_ups` table
|
||||
4. Add outbound triage prompt to K2.5
|
||||
5. Add follow-up check to hourly cron / heartbeat
|
||||
6. Add auto-resolve on inbound match
|
||||
7. Add dismiss callback endpoint in MC
|
||||
8. Wire alert dashboard to call MC on dismiss
|
||||
9. Add auto-expire cleanup
|
||||
|
||||
## Not In Scope (Yet)
|
||||
|
||||
- WhatsApp outbound tracking (could add later, same pattern)
|
||||
- Calendar meeting follow-ups (different trigger, same tracker)
|
||||
- Priority escalation (if 2x deadline passes, escalate from 🟠 to 🔴)
|
||||
Loading…
Reference in New Issue