---
name: "inbox_zero_systems"
description: "Run a repeatable daily and weekly inbox triage operating system on the user's email account: batch windows, a reply/delegate/archive/unsubscribe decision matrix, unsubscribe sweeps with real search operators, and delegation-cue detection. Triggers when someone asks to clean up their email, set up inbox triage, tame newsletters, or keep a mailbox at zero."
metadata: { "includeInPrompt": true }
---

# Inbox Zero Systems

## Purpose

Give the user an operating system for their mailbox, not a one-time cleanup. The agent runs recurring triage batches against the user's email (Gmail or Outlook style): scan new mail, classify each message with a fixed decision matrix, take the safe actions, draft the rest, and run a weekly unsubscribe sweep. This is the receiver side: what to do with mail that arrives. It does not cover sender-side deliverability (SPF, DKIM, warmup) or writing newsletters.

## Workflow

1. **Discover the mailbox.** Ask which account to triage, how the agent reads it (mail skill, API, or manual export), rough daily volume, and standing permissions: what may be archived or labeled freely, and what needs per-item approval. Never assume permission to send, unsubscribe, or delete.
2. **Set the batch cadence** from the table below. Start with the defaults and adjust to volume.
3. **Run each batch.** Pull mail since the last batch timestamp. Build `messages.json` (one object per message: id, sender, subject, snippet, days_old, opened_recently, has_unsubscribe, is_promo). Run the triage script to get a first-pass plan, then review every decision yourself. The script is a sketch aid, not a verdict.
4. **Apply the decision matrix.** One action per message, from the table below.
5. **Execute in two tiers.** Tier A (reversible): archive, label, mark read, when the user has granted mailbox-management permission. Tier B (outward-facing): reply, delegate, unsubscribe. Draft these and propose them; execute only on the user's approval for that item, or when a standing directive names that exact sender or class.
6. **Run the weekly sweep.** Unsubscribe queries, filter audit, delegation review. Weekly, not daily, so mail the user might still want does not get churned.
7. **Maintain filters.** When a pattern recurs (same sender archived three batches in a row), propose a filter: Gmail Settings > Filters and Blocked Addresses, created manually or by an approved browser step. Sketch the filter rule; do not claim a CLI exists for this.

### Batch cadence

| Window | When | Timebox | Scope |
|---|---|---|---|
| Morning sweep | Start of workday | 25 min | Everything since last close; end with inbox empty or an explicit carry-over list |
| Midday window | After lunch | 15 min | New arrivals only since the morning sweep |
| End-of-day close | End of workday | 10 min | Check the reply queue, set tomorrow's follow-ups, confirm zero or carry-overs |
| Weekly sweep | Friday or Monday | 30 min | Unsubscribe queries, filter audit, delegation review |

### Decision matrix

| Action | When the message ... | Agent does |
|---|---|---|
| reply | Asks the user a direct question, needs their decision or sign-off, or names a deadline | Draft the reply; priority "today" when an urgency word or date is present |
| delegate | Contains an ask that belongs to someone else (see cues below) | Draft a handoff naming the owner, the ask, and the deadline; send only on approval |
| archive | Is a notification with no action needed: receipts, shipping, password changes, FYI with no ask | Archive (and label if the user keeps a label system), Tier A |
| unsubscribe | Is promo/newsletter mail with an unsubscribe link, unopened for 2+ weeks | Propose it; unsubscribe only on approval or standing directive |
| read-later | Is promo the user still opens, or has no clear signal | Batch into a weekly digest read; never archive mail you cannot classify |

### Delegation cues

Treat as delegation candidates when the snippet contains any of these, or when the user is CC'd and never named:

- "can you", "could you", "please handle/send/review/forward"
- "action required from", "for your review/approval/action"
- "fyi" with an attached doc and no explicit ask
- Calendar invites or scheduling threads for someone else's event
- Vendor follow-ups ("per your request") that need no user action

A delegation draft states three things: who owns it, what the ask is, and by when. Without all three, it stays a proposal.

### Unsubscribe sweep queries (Gmail)

These operators are stable and safe to quote. Adjust the age windows to the user's tolerance.

- `"unsubscribe" category:promotions older_than:30d` (promo mail piling up)
- `"unsubscribe" -is:starred older_than:90d` (old list mail never starred)
- `from:(newsletter OR digest OR deals OR offers) older_than:60d`
- `category:updates "unsubscribe" older_than:90d`

For each hit, check open history: unopened for 2+ weeks and carrying an unsubscribe link goes on the proposal list. Mail the user still opens stays, and gets read in the weekly digest batch instead. Outlook web supports similar filters (`from:`, `to:`, `subject:`, `hasattachments:`, `received:`); confirm the exact syntax in the user's client before relying on it, expected shape may differ.

### Triage plan script

Save the block below as `triage_plan.py` in your working directory, then run `python3 triage_plan.py messages.json`. It reads the message list and prints a JSON plan with one action, priority, and reason per message. Review the plan before acting on it.

```python
#!/usr/bin/env python3
"""triage_plan.py: turn a message list into a concrete triage plan.

Usage:  python3 triage_plan.py messages.json
Input:  JSON array of {id, sender, subject, snippet, days_old,
        opened_recently, has_unsubscribe, is_promo}
Output: JSON array of {id, subject, action, priority, reason}
Actions: reply, delegate, archive, unsubscribe, read-later
"""
import json
import re
import sys

DELEGATE_RE = re.compile(
    r"\b(can you|could you|please (handle|send|review|forward|take care of))\b"
    r"|\baction (required|needed) from\b|\bfor your (review|approval|action)\b|\bfyi\b",
    re.I,
)
REPLY_RE = re.compile(
    r"\b(what do you think|your thoughts|need your (input|sign-?off|approval)|question for you)\b",
    re.I,
)
URGENT_RE = re.compile(r"\b(urgent|asap|by (eod|cob|friday|monday|today)|deadline)\b", re.I)
NOISE_RE = re.compile(
    r"\b(receipt|order (shipped|confirmed|delivered)|password (reset|changed)|"
    r"weekly digest|do not reply|no-reply|shipment tracking)\b",
    re.I,
)
BOT_SENDER_RE = re.compile(r"(no-?reply|donotreply|newsletter|digest|deals|offers|promo)", re.I)


def decide(m):
    snippet = m.get("snippet", "")
    sender = m.get("sender", "")
    is_promo = m.get("is_promo", False)
    opened = m.get("opened_recently", False)
    unsub = m.get("has_unsubscribe", False)
    age = m.get("days_old", 0)

    if is_promo and unsub and not opened and age >= 14:
        return ("unsubscribe", "normal", "promo mail, no opens in 2+ weeks")
    if DELEGATE_RE.search(snippet):
        return ("delegate", "normal", "message asks for an action that belongs to someone else")
    if REPLY_RE.search(snippet):
        pri = "today" if URGENT_RE.search(snippet) else "normal"
        return ("reply", pri, "direct question or decision addressed to the user")
    if is_promo and opened:
        return ("read-later", "normal", "promo the user still opens; batch into weekly digest read")
    if NOISE_RE.search(snippet) or (BOT_SENDER_RE.search(sender) and not is_promo):
        return ("archive", "normal", "notification with no action needed")
    return ("read-later", "normal", "no clear signal; needs human eyes")


def main(path):
    msgs = json.load(open(path))
    plan = []
    for m in msgs:
        action, priority, reason = decide(m)
        plan.append(
            {"id": m["id"], "subject": m["subject"], "action": action,
             "priority": priority, "reason": reason}
        )
    print(json.dumps(plan, indent=2))


if __name__ == "__main__":
    main(sys.argv[1])
```

## Output Contract

Every batch run returns a short report with these parts, in this order:

1. **Counts:** messages scanned, per action (replied, delegated, archived, unsubscribed, read-later).
2. **Decisions table:** id, sender, subject, action, priority, reason. One row per message.
3. **Tier A done:** which archives/labels were applied.
4. **Tier B proposed:** reply drafts, delegation drafts (owner, ask, deadline), unsubscribe proposal list. Nothing sent without approval.
5. **Carry-overs:** messages not at zero, with the reason and the next batch they fall into.

A run is finished when the inbox is empty or every remaining message has a named carry-over.

## Operating Rules

1. When a message's intent is unclear, mark it read-later. Never archive mail you cannot classify.
2. When no standing permission exists, propose Tier B actions (reply, delegate, unsubscribe) and wait. Execute them only on the user's approval for that item, or when a standing directive names that exact sender or class.
3. When the user has granted mailbox-management permission, Tier A actions (archive, label, mark read) may run without per-item approval.
4. Track the last batch timestamp. One pass per window; do not re-triage the same mail.
5. When a sender is archived three batches in a row, propose a filter for them instead of archiving a fourth time.
6. When the user's volume doubles, shorten the cadence windows' scope (new arrivals only) rather than skipping batches.
7. When a delegation draft cannot name an owner, an ask, and a deadline, it stays a proposal and goes back to the user as a question.
8. Keep the triage script's patterns as a starting set. When the user corrects a decision twice for the same pattern, update the pattern.
