● Shipped P0 Size M Vertical app

Mail-Assistant — ADHD-first AI Inbox Triage

A local-first AI inbox triage layer that classifies threads P0/P1/P2/NOISE with cross-channel verification — cutting daily triage from 90 minutes to 8 minutes.

A personal email triage agent designed for an ADHD brain. Reads 3 inboxes via IMAP, classifies every thread with a cross-channel sanity check (Jira status + Sent-folder staleness), and surfaces only what actually needs action — in a single native macOS surface. Default class is NOISE; the classifier must justify promotion.

At a glance

  • 568 daily messages → 7 P0/P1 rows on a typical day
  • Triage time: ~90 min → ~8 min after 4 weeks of iteration
  • False-positive P0: 7–9/day → 2–3/day after cross-channel verification landed
  • Single surface — a native SwiftUI macOS app (no Electron, no Slack digest, no mobile push)
  • 3-section collapsible UI — P0 / P1 / P2 with Done + Archive shortcuts (no Snooze)
  • Backend — FastAPI + 3 systemd timers (poll-imap, classify-batch, sync-actions) on a small commodity VM
  • Database — Postgres 16 with three tables: threads, classifications, actions
  • Classifier — Claude Haiku 4.5 with thread-latest-only input, share-notification hard-rules, future-tense gate
  • Cost — ~$1.20/month in classifier tokens after subset-test discipline
  • Reachable — private HTTPS endpoint via a Cloudflare named tunnel; bearer auth + optional CF Access on the UI

Stack

Python 3.11 · FastAPI · Postgres 16 · systemd timers · Claude Haiku 4.5 · IMAP (imaplib) · Gmail API (for label/archive sync) · Jira REST · Swift + SwiftUI (native macOS, NSPanel + Liquid Glass aesthetic) · Cloudflare Tunnel

Documentation

DocRead this for
PRDWhat & why — problem framing, JTBD, goals, scope, milestones
ArchitectureSystem diagrams, data flow (pull → classify → surface → action), failure modes
ImplementationTech stack, schema, classifier prompt design, perf numbers, security model
NotesChronological decision log + the reclassify cost trap

Quickstart for the operator

# 1. Provision a small commodity VM (1 vCPU / 1 GB is enough)
# 2. Postgres 16 + 3 systemd timers (poll-imap, classify-batch, sync-actions)
# 3. Drop IMAP creds + Gmail OAuth token + Jira PAT into /etc/mail-assistant/
# 4. cloudflared named tunnel → https://<your-host>
# 5. Build the Mac app:
xcodebuild -project MailAssistant.xcodeproj -scheme MailAssistant -configuration Release
# 6. Open Mail-Assistant.app → enter bearer token → triage

Project status

DayMilestone
1IMAP poller + Postgres schema + naive 4-class classifier (P0/P1/P2/Archive)
1Default flipped to NOISE; classifier must justify promotion
1-2Cross-channel checks added (Jira status + Sent-folder staleness)
2Thread-latest-only classification (drop history)
2Share-notification hard-rule (Sheets/Notion/Dropbox/Figma)
2SwiftUI app — 3 collapsible sections, Done/Archive shortcuts
2Action sync — Done/Archive propagates to Gmail (archive INBOX + label)
2Deployment: cron-host VM + Cloudflare named tunnel

Total build time: 2 days concentrated work.

Why this exists

Inbox triage was costing 90 minutes per morning of high-fatigue context-switching before any real work happened. Off-the-shelf options (Gmail Priority Inbox, SaneBox, Superhuman) optimize for click-through, not “needs reply” — and none of them know that the Jira ticket the email refers to is already Done, or that I already replied 6 minutes ago from my phone. Mail-Assistant is built around that missing context layer.

📚

STACK

  • Python 3.11
  • FastAPI
  • Postgres 16
  • systemd timers
  • Claude Haiku 4.5
  • IMAP
  • Gmail API
  • Jira REST
  • Swift / SwiftUI
  • Cloudflare Tunnel