Skip to content

Inbox Sweep

A rule-based bulk email triage console at /workshop/pages/sweep/. The goal is narrow and measurable:

Inbox zero in under a minute a day β€” approve a rule once, never see that sender again; residue is only mail that needs a human.

Status: shipped 2026-07-21. vita.email.* namespace (9 capabilities) over scripts/email_sweep/. Verified live including a net-zero write test.

Why the previous attempts died

bio-zack's work inbox hit 23,800 messages, 68% of it one class of server alerts. He bulk-archives in his head but never in Gmail.

The classifier was never the problem. Vita had built an email client (Mercury) before, and the autopsy named four specific reasons it went cold:

Failure What it meant
It was a destination client competing with Gmail, which always wins
Trust-gated archives archiving was staged/simulated, i.e. fake β€” so he still had to go do it for real
Stale queue counts the number on screen wasn't the number in Gmail
No bulk one-tap, no undo so the risky action was never worth taking

Inbox Sweep is built as the inverse of each: a console, not a client; real batchModify archives; live counts straight from Gmail; one tap with a real undo.

How it works

  discovery (sampled, cached <1h)
        β”‚  newest inbox mail per account
        β–Ό
  candidate rules  ── sender / subject β†’ archive
        β”‚           + "needs you" human residue (matches no rule)
        β–Ό
  rules store  data/email/triage_rules.json
        β”‚  status: proposed β†’ approved β†’ (dismissed)
        β–Ό
  preview  ── live match count + samples, read-only
        β”‚
        β–Ό
  sweep  ── real Gmail batchModify, removes INBOX label
        β”‚   ≀10k per rule, `-is:starred` ALWAYS excluded
        β–Ό
  ledger  data/email/sweeps.jsonl (append-only)
        β”‚
        β–Ό
  undo  ── re-add INBOX to exactly those message ids
           (refuses a double-undo β€” the ledger is append-only)

Design decisions that carry the trust

Counts are never cached. vita.email.overview calls the Gmail labels.get API for exact messagesTotal / messagesUnread per account β€” it never reads email.json or any vita-side cache. A console whose numbers disagree with Gmail is worthless.

Only approved rules can sweep. New rules default to status=proposed. vita.email.sweep refuses proposed/dismissed/unknown rules with invalid_input listing the offenders β€” and archives nothing in that case, rather than partially applying.

Starred is always excluded. -is:starred is appended unconditionally, not as a user option.

Every sweep is undoable. Each run's message ids are ledgered, so undo re-adds the INBOX label to exactly those messages. The raw ids are never returned through the capability surface β€” only a human rule_summary, the archived count, and whether it was undone.

The capability surface

Capability Kind What it does
vita.email.overview read live per-account inbox totals from Gmail
vita.email.discover read sample newest mail β†’ propose rules + "needs you" residue
vita.email.rules read full rules list (proposed / approved / dismissed)
vita.email.rule_preview read live match count + samples for a rule
vita.email.rule_upsert write create/update a rule
vita.email.rule_delete write delete a rule
vita.email.sweep write bulk-archive matches of approved rules
vita.email.sweeps read recent ledger entries, with cross-session undo
vita.email.undo_sweep write restore exactly what a sweep archived

Because it's built on the capability layer, the same nine operations are available to the workshop page, to any agent, and to the CLI without a second implementation.

Seeding

32 rules were seeded from a recon pass over the real inbox, matching roughly 24,000 messages β€” all in proposed state. Each shows a live preview count and sample messages before bio-zack approves anything.

Per-rule auto is opt-in: when set, the rule sweeps on page visit.

Signals

Working: he approves the seeds and the first sweep clears ~80%; repeat visits every day-or-two keep inbox_total near the human-residue floor; new candidate rules get approved or dismissed rather than ignored; undo exists but goes unused (trust earned).

Failing: counts climb back and the page goes cold, like Mercury did.

Not built

Deliberately deferred until daily use earns them: scheduled server-side auto-sweep of auto rules, a telegram nudge ("N archivable β€” sweep?"), and a needs-you β†’ task hook.