# Inbox Info

> Research and answer questions about email in the user's Maildir via mu (maildir-utils). Use proactively when: (1) user asks about emails, inbox, flagged messages, senders, subjects, or specific correspondence, (2) user asks "what do I need to handle / respond to?" — flagged emails are the user's marker for actionable items, (3) user mentions mu, mutt, neomutt, or Maildir, (4) user wants to convert email-derived work into nytid todos. Strictly read-only; never modifies mail state.

- Skill: `dbosk/inbox-info` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add dbosk/inbox-info`
- Raw SKILL.md: https://api.skillmd.com/api/skills/dbosk/inbox-info/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Research & Search
- Author: dbosk (https://skillmd.com/u/dbosk)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/dbosk/inbox-info

---


# Inbox Information Research

## Overview

The user reads email with NeoMutt against a Maildir indexed by `mu`
(maildir-utils). This skill wraps `mu find` / `mu view` to answer questions
about the inbox without scanning Maildir files directly. It also helps
convert flagged emails into themed `nytid todo` items.

**Canonical "needs handling" signal:** the user **flags** every email they
need to act on (the `\Flagged` / "starred" IMAP flag). Default to
`flag:flagged` for triage queries — not `flag:unread`.

This skill is **strictly read-only**: query `mu`, suggest `nytid todo`
commands, and let NeoMutt own flag/state changes.

## Known Setup

Embedded so the skill activates with zero tool calls. Re-verify with
`mu info` only if a query misbehaves.

- **Maildir+ account root** (use with `mu` — it indexes the whole tree):
  `/home/dbosk/mail/kth`
- **Key leaf folders** — encode the lifecycle of a message, not just
  routing:
  - `INBOX/` — **waiting / not yet processed**. Unread mail lives here
    by definition; flagged mail here is *active work*. **For triage,
    this is the folder to target.**
  - `Processed/` — **done**. Messages the user has handled and moved
    out of INBOX. The user *intentionally keeps flags here* — a flag
    in Processed means "I had to act on this", retained as a labeled
    signal for tuning `~/.muttrc.scores` and spotting patterns. **Do
    not** treat them as actionable triage items, but **do not** ignore
    them as noise either — they're training data.
  - `Processed-archived/` — **archived, not interesting**. Older
    Processed mail. Even less relevant than `Processed/` for triage.
- **Xapian DB**: `/home/dbosk/.cache/mu/xapian`
- **Verified**: 2026-05-27

The account root contains subfolders (`INBOX/`, `Sent/`, `Archive/`,
`Processed/`, `Processed-archived/`, etc.) but no `cur/new/tmp` of its
own. Pointing `neomutt -f` at the root opens the folder browser, not
messages — limits then filter an empty set. Always target a specific
leaf for mutt.

**Implication for triage queries:** unread mail = "waiting to be
processed" (the user moves mail to `Processed/` when done; unread mail
still in INBOX is therefore by-definition actionable). Old unread mail
in INBOX is *stale* — never got around to it. Flagged mail in
`Processed/` is *not* a triage target — it's done — but it *is* a
historical record of "things that needed action", deliberately
retained for score-tuning. Use the user's flag-discipline to your
advantage:

- **Flag-cleared anywhere** = "I didn't have to act on this" (FYI/noise).
- **Flagged in INBOX** = "this needs action, still pending".
- **Flagged in Processed/** = "this needed action and is now done"
  (labeled training point).

**Lazy fallback — call `mu info` only if:**
- a query returns unexpectedly zero results,
- the user reports the index seems stale,
- or `mu find` errors out.

## Research Workflow

### Step 1: Pick the right query

| Question | Query |
|----------|-------|
| "What needs handling?" | `mu find 'flag:flagged' --fields 'd f s' --sortfield date --reverse` |
| "What's been flagged forever?" | `mu find 'flag:flagged AND date:..1m' --fields 'd f s' --sortfield date` |
| "Recent from X?" | `mu find 'from:NAME date:1m..' --fields 'd f s' --maxnum 20` |
| "Anything on topic Y?" | `mu find 'subject:Y OR body:Y' --threads --fields 'd f s'` |
| "How many of Z?" | `mu find QUERY --fields i \| wc -l` |
| "Show me message N" | `mu view <path>` (path from `--fields l`) |

Bound output: always use `--maxnum N` and `--fields` to keep results compact.

### Step 2: Classify against the user's taxonomy

The authoritative classifier lives in `~/.muttrc.scores`. For the full
extracted taxonomy — every category regex, every high-signal sender,
every noise filter — see
`references/muttrc-scores-taxonomy.md`. Read it when classification is
non-trivial or when the user updates their scoring config.

The common categories (priority order) are:

| Category | Subject regex | Notes |
|----------|---------------|-------|
| **Missing grades / Ladok** | `ladok\|betyg\|tillgodoräk` | **First-class — always its own bucket** |
| Urgent | `brådska\|bråttom\|urgent\|viktig\|important` | |
| TA management | `assning\|asse\|assistent\|\bTA\b` | |
| Course operations | `kompletter\|examin\|rättning\|redovis\|labb?\|rapport\|rest(er\|labb\|uppgift\|inlämn)` | grading, make-ups, lab reports |
| Course analysis / PM | `(kurs)?.?(analys\|PM\|pm\|utvärdering)` | |
| Timesheets / pay | `tidrapport\|timersättning\|timesheet\|lön\|timmar\|arvode` | |
| Sick / personal | `sjuk\|sick\|\bill\b` | |
| Reviewing | `reviewer.*invitation\|invitation.*reviewer` | |
| Scheduling | from `schema@admin.kth.se` or `@timeedit.com` | |

**Course codes** to recognize:
- DD1301 (`datorintro`/`dataintro`)
- DD131[057] (`programmering`/`prg`)
- DD1320 (`tilda`)
- DD1337 (`inda`)
- DD2520 (`tilkry`/`tillkry`)
- DA2215, DD2302 (`vetcyb`, science/security)
- DA150X (`dkex`/`dkand`/`kex`, bachelor thesis)
- DA231X (`exjobb`, master thesis)
- LH219V (pedagogy)

**Programme tags** to recognize: `CINEK|CMAST|CITEH|CDATE|CFATE|CTMAT|CTFYS|CLGYM`.

**High-signal senders** (from `~/.muttrc.scores`): Celina Soori, Yara Faris,
Yasmina Altayy, Liisa Wettemark, Edita Korlat, Francisco Gomes, Bedour
Alshaigy, plus institutional addresses (`schema@admin.kth.se`,
`service@eecs.kth.se`, `education-support@eecs.kth.se`, etc.). When a
flagged email is from one of these, give it its own cluster.

**Known noise** (excluded from triage even if flagged): Google Scholar,
KTH Magazine, IEEE, ACM, SIAM, Nature Briefing, The Download, KTH Alumni,
etc. — see negative scores in `~/.muttrc.scores`.

### Step 3: Handle ambiguous subjects (students often write bad ones)

Many emails arrive with vague subjects: "Hi", "Hej", "Question", "Help",
"Course", a bare first name, or just a course code. Don't trust subject
regex alone. Two-tier approach:

1. **Subject regex first** (cheap) — sort flagged emails into the
   obvious buckets.
2. **Body query for ambiguous matches**:
   `mu find 'flag:flagged AND body:/ladok|betyg/' --fields 'd f s'`
   catches grade-adjacent emails whose subjects don't mention grades.
3. **Read the full message** with `mu view <path>` only for the
   borderline cases — never as a default step.
4. If still unclassified, put it in a residual **"uncategorized flagged"**
   group rather than forcing a wrong bucket.

### Step 4: Group flagged emails into themed todos

When the user asks for todos from flagged email, propose **themed batches**
— never one todo per message. Cluster by category, course code, sender, or
topic, then suggest one `nytid todo add` per group.

For the full rules with rationale and worked examples, see
`references/todo-conversion-rules.md`. Headlines:

- **NEVER use `--who dan-claude`.** Email-derived todos are for the user
  themselves. dan-claude doesn't answer the user's email. Omit `--who`
  entirely so the default (current user) applies.
- **Missing grades is always its own group**, separate from other course
  traffic, even when only 1–2 messages match.
- **Every todo description must embed a review command** so the user can
  re-open the relevant emails with one paste.

### Step 5: Choose the right review-command form

Two forms. Default to **Form A** unless you specifically need mu's richer
query language for read-only browsing.

**Form A — mutt `limit` on the real Maildir (DEFAULT).** State changes
(mark read, reply, unflag) persist to the real inbox:

```
neomutt -f /home/dbosk/mail/kth/INBOX \
  -e 'push "<limit>~F ~s \"betyg\\|ladok\\|grade\"<enter>"'
```

Note `INBOX`, not the account root — see "Known Setup" above.

Mutt patterns: `~F` flagged, `~s "regex"` subject, `~b "regex"` body,
`~f "regex"` from, `!~s "regex"` negation. Combine with space (AND) or `|` (OR
within a single regex). **Date filters**: `~d "<3m"` newer than 3 months ago
(within last 3 months), `~d ">3m"` older than 3 months ago. Offsets: `d` day,
`w` week, `m` month, `y` year. Absolute range: `~d "01/27/26-02/27/26"` (US
`MM/DD/YY`). When embedding in a shell string, escape inner double quotes:
`~d \"<3m\"`.

**Form B — mu virtual maildir, READ-ONLY browsing only.** Use when a
query can't be expressed as mutt patterns (cross-folder, mu's richer
operators):

```
mu find 'flag:flagged AND (subject:/grade|betyg|ladok/ OR body:/ladok|betyg/)' \
  --format links --linksdir ~/.cache/mu-results/missing-grades --clearlinks \
  && neomutt -f ~/.cache/mu-results/missing-grades
```

**Form B caveat — symlinks don't propagate state.** Marking a message
as read in the linksdir renames the *symlink*, not the underlying file.
State changes do NOT reach the real inbox. Pick Form A whenever the
user will mutate state.

Use a per-group slug under `~/.cache/mu-results/<slug>/` for Form B
so concurrent uses don't collide.

### Step 6: Suggest the todo (don't run it)

Print the command for the user to run. **Prefer `--command` (`-c`)** over
`--description` for the review command — that way `nytid todo start <id>`
runs it directly, no copy-paste. Put context/rationale in `--description`
for `view`, put the executable command in `-c`.

```
nytid todo add MAIL \
  -t "Resolve missing grades — 5 student emails" \
  --description "5 flagged emails about missing grades (Ladok/betyg)." \
  -c "neomutt -f /home/dbosk/mail/kth/INBOX -e 'push \"<limit>~F ~s \\\"betyg|ladok|grade\\\"<enter>\"'"
```

**Escape ladder** (bash → nytid storage → shell on `start` → mutt `-e`
parser → mutt push string). Inside a bash double-quoted argument, use
`\\\"` for every literal `"` that mutt's push-string parser must see — `\\`
becomes `\` and `\"` becomes `"`, yielding the stored `\"` that mutt
unescapes back to `"`. When you splice variables into the template, make
sure they're *bare* (no quotes baked in) and let the template add the
`\\\"` around them.

For adding email context to an existing todo, suggest:
`nytid todo note <ID> -m "Saw a follow-up email from <sender> on <date>"`.

See the `nytid-todo` skill for broader todo-workflow details, including
auto-parenting behavior (a new `nytid todo add` without `--top-level`
parents under the currently active todo — handy when the user has an
"Clear inbox" todo in-progress and wants each bucket as a child).

## Quick Reference

| Operator | Meaning |
|----------|---------|
| `from:NAME` | sender match |
| `to:NAME` | recipient match |
| `subject:TEXT` | subject contains |
| `body:TEXT` | body contains |
| `flag:flagged` | starred / actionable |
| `flag:unread` | unread |
| `date:1w..` | last week to now |
| `date:..1m` | older than 1 month |
| `date:20260101..20260131` | absolute range |
| `mime:TYPE` | attachment mime type |
| `AND` / `OR` / `NOT` | boolean combiners |

`--fields` letters: `d` date, `f` from, `t` to, `s` subject, `l` path,
`i` message-id, `g` flags.

## Output Handling

- Default fields: `d f s`. Add `l` only when feeding `mu view`.
- For large result sets, summarize aggregates ("12 flagged from this
  month, mostly course ops") instead of dumping the full list.
- Use `--threads` for topic/thread questions.
- Output is tab-delimited by default; pipe through
  `column -t -s$'\t'` for readability when showing to the user.

## Scope

**Strictly read-only.** Never run `mu index`, `mu remove`, `mu mkdir`, or
anything that writes to the Maildir or Xapian DB. Never compose, send, or
modify mail state — defer to NeoMutt. Never create `nytid todo` items
directly; only suggest commands. Don't print email bodies unless the
user asks.

## Common Pitfalls

**Stale index.** Results miss new mail until `mu index` runs. If the
user reports missing emails, run `mu info` to check the last index time
and suggest they reindex.

**Shell quoting.** Always single-quote `mu` queries: `'flag:flagged'`,
`'from:foo AND date:1w..'`.

**mu regex `/.../` is fragile.** Spaces and parentheses inside the slashes
silently match zero results — e.g. `subject:/applied crypto/` and
`subject:/(foo|bar)/` both return nothing instead of erroring. For
multi-word terms or grouped alternations, use boolean OR of word terms:
`(subject:applied OR subject:crypto)`, `(subject:foo OR subject:bar)`.
Single-word alternations like `subject:/foo|bar|baz/` do work. When a
count looks suspiciously low, sanity-check by running each clause
separately with `--fields i | wc -l`.

**Folder lifecycle is asymmetric — flag in `Processed/` ≠ actionable.**
Tempting reflex: query `flag:flagged` everywhere and assume each hit
is a triage target. Wrong here. Flags in `Processed/` and
`Processed-archived/` are *intentional* — the user keeps them as a
labeled "had-to-act" signal for tuning `~/.muttrc.scores`. They are
not noise to ignore, but they are not work-to-do either. The only
triage-relevant folder is `INBOX/`. **Always constrain mu queries
with `maildir:/INBOX`** when building todos for "what needs handling":

```
mu find 'flag:unread AND maildir:/INBOX AND date:3m.. AND <category>'
```

If a folder-distribution check is ever needed (e.g. to confirm a
category really is empty in INBOX vs. just sitting in Processed):

```
mu find '<query>' --fields l --maxnum 500 \
  | sed 's|^/home/dbosk/mail/kth/||; s|/cur/.*||; s|/new/.*||' \
  | sort | uniq -c | sort -rn
```

A bucket with zero unread/flagged in INBOX is *correctly empty* — the
user has already processed it (the data is in Processed/, which is
done). Don't try to "rescue" Processed mail into a triage view.

**Filter choice cheat-sheet** (all assume `maildir:/INBOX`):

| Goal | Filter | Notes |
|---|---|---|
| Waiting to be processed | `~U` | Canonical signal; INBOX-resident |
| Manually-marked important & waiting | `~F` | Subset of unread+read in INBOX |
| Most-pressing | `~U ~F` | Important AND not yet opened |
| Stale to-do (never finished) | `~U ~d ">3m"` or `~F ~d ">3m"` | Months in INBOX = forgotten |

### Score-tuning queries (use Processed flagged mail)

If the user asks "which senders/subjects keep needing action?" or
wants to improve `~/.muttrc.scores`, that's exactly what flagged
mail in `Processed/` is for — it's the user's hand-labeled corpus
of "things that turned out to need action". Useful queries:

```
# Top senders of acted-on mail in the last year
mu find 'flag:flagged AND maildir:/Processed AND date:1y..' --fields f \
  | sort | uniq -c | sort -rn | head -20

# Common subject tokens in acted-on mail
mu find 'flag:flagged AND maildir:/Processed AND date:6m..' --fields s \
  | tr ' ' '\n' | sort | uniq -c | sort -rn | head -30
```

Compare against `~/.muttrc.scores` to find under-scored senders/terms.

**Form B is read-only.** Symlink renames stay in the linksdir; the real
inbox is untouched. Use Form A whenever state must persist.

**Trusting subjects.** Student emails frequently have unhelpful subjects.
Use body queries or `mu view` for ambiguous cases.

**Wrong todo ownership.** Never assign email-derived todos to
`dan-claude`. They belong to the user.

