SieveEmail Triage Agent
What it does for you
Your inbox sorted: urgent, can wait, and scams.
An email triage agent that sorts an inbox into eight buckets with an explainable score, flags phishing links, and leaves reply drafts for review, with a guard that never lets it send, delete, or label on its own.

Who it's for
Owners of small shops who spend the first hour of the day sorting email. It explains why each message landed where it did, and it can't send or delete anything on its own.
Screenshots

In more detail
Sieve reads a small business inbox, tells you what needs you first and why, flags the phishing, and leaves reply drafts ready for you to review. It never sends anything. A small consulting shop gets client questions, invoices, invites, pitches, newsletters, and the occasional phishing email all in one pile, and the owner spends the first hour of the day sorting it. An agent can do that sorting, but only if it explains its ranking and can't send or delete on its own.
Honest limits
Sieve never touched a real mailbox. It runs on a fictional inbox with a fixed "now" for repeatable demos, and the Gmail skill is written but wasn't run against a live account during the build. The rules are an if/else chain, and a real inbox would expose things a fixture can't, like a known contact who marks everything urgent.
How I built it
This was a Claude Cowork build. My instruction was "one shot all the Cowork projects," and Claude Cowork took this brief (triage categories, an explainable score, phishing checks, the draft-only rule, a morning digest, and a skill file) and specced, built, tested, screenshotted, and documented it in one pass. A separate Cowork session dropped its finished builds into the repo and merged them into the tracker itself. Setting up in Cowork felt super fast to me. A Cowork plugin is probably faster to set up than a Claude Code build.
Two halves share one loop: fetch, classify, summarize, draft, review. The browser app runs on a fictional 32-thread inbox, with a DOM-free core (sieve.js) imported by both the page and the tests. The cowork-skill/SKILL.md maps the same loop onto the Gmail connector (search_threads, get_thread, create_draft). Each thread lands in one of eight categories with a 0 to 100 score built from visible reasons. Drafts come from ten intent templates and go through a validator: no em dashes, length limits, no dates or amounts that aren't in the thread, no fake "attached" claims. Cowork also read its own screenshots and fixed what they showed, like a mobile list overflowing to 498px.
What I learned
- The agent's permissions should be data, not prose. Every Gmail tool sits in a policy table (read, draft, confirm, or never) behind one guard function. The loop tries three forbidden actions on every run as a self-test, and the tests prove "never" tools stay blocked even when an action claims to be confirmed.
- A draft validator has to read dates the way the thread does. The parser treated the weekday in "Thursday, September 24" as its own date, so my own nudge template failed its own check.
- "From our domain" isn't the same as "we sent it." A teammate's internal note was being filed as waiting on them until outbound meant from us to someone outside the team.
- A vendor pitch that ends in a question looks exactly like a client question. Checking sender history first keeps it out of Needs reply.
What carried forward
The draft-only guard is the idea I'd reuse first. It turns a safety promise into something the tests can prove. The optional Claude mode drafts replies as JSON and runs them through the same local validator, the same "Claude proposes, local code checks" shape used in Ledgerly (67) and Winnow (70). The no-em-dash scan that caught stray dashes in sieve.js and verify.mjs is the same hygiene check every project in the sprint had to pass.
If I took it further
I'd move the classification rules into an ordered table users can edit, and add a "this was not urgent" button that adjusts per-sender weight.
#069 Built with Claude Cowork September 23, 2026 323 automated checks