# Inbox

> Inbox Skill

- Skill: `tandregbg/inbox` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tandregbg/inbox`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tandregbg/inbox/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tandregbg (https://skillmd.com/u/tandregbg)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/tandregbg/inbox

---


# Inbox Skill

Universal capture and classification for unstructured content. Accepts voice memos, quick notes, raw text, forwarded emails -- classifies and routes to the appropriate downstream skill.

## Vault Location

```
vault/_inbox/
  _inbox.yaml           # Derived index (rebuilt from frontmatter)
  YYMMDD-HHMMSS[-slug].md   # Active items (frontmatter is canonical)
  .audio/               # Raw audio captured by Trillian, paired by basename
    YYMMDD-HHMMSS[-slug].m4a
  .files/               # File drops: input files with a vault destiny (CR-024)
    YYMMDD-HHMMSS[-slug].<ext>
  .archive/             # Processed items (moved here after downstream skill runs)
    .audio/             # Audio archived alongside transcript
    .files/             # Source files that don't accompany their output
```

**Schema**: see [`docs/schemas/inbox.md`](../../docs/schemas/inbox.md) for
the canonical `<id>.md` frontmatter shape, `.audio/` convention, and
lifecycle states. The schema is the source of truth; this SKILL.md
documents skill behaviour. Other tools (Trillian, Deep Thought, Marvin,
external skills) write directly to `_inbox/` against the schema; `/inbox`
is one of several producers.

## Subcommands

### `/inbox [content]` -- Default: Capture + Classify

Accept raw content (pasted text, file path, or inline text), classify it, store in `_inbox/`, and suggest the downstream skill.

**Processing flow:**

1. **Parse input** -- detect content type:
   - Multiple speakers, timestamps, speaker labels -> `voice_memo`
   - "From:", "Subject:", email headers -> `email`
   - File path provided -> read the file, detect type from content
   - **Binary or non-markdown file path** (PDF, CSV/xlsx, image, media, archive) -> `file_drop` (CR-024): move/copy the file into `_inbox/.files/<id>.<ext>` and create the paired stub with `source.file_path` and a type-based classification guess (PDF/document → summarize into a folder; CSV/xlsx → project data; image/media → attachment). Files >~25 MB: flag the size and offer process-in-place + reference-stub archiving instead of moving the blob. Full convention in [`docs/schemas/inbox.md`](../../docs/schemas/inbox.md) "File drops".
   - Short text, no structure -> `quick_note`
   - Everything else -> `raw_text`

2. **Auto-classify** -- determine which downstream skill should process this:

   | Signal | Classification | Target Skill |
   |--------|---------------|-------------|
   | Speaker labels, timestamps, dialogue format | `transcript` | `/transcript` |
   | Org team member names (from ops-config), meeting context | `ops` | `/ops` |
   | "TODO", "remind me", imperative sentences, action items | `task` | `/tasks add` |
   | Short observation, no action context | `note` | None (already saved) |
   | Idea, brainstorm, "what if" | `idea` | None (already saved) |

   Assign confidence: `high` (clear signals), `medium` (some signals), `low` (ambiguous).

3. **Determine routing** -- consult CLAUDE.md MEETING ROUTING for folder suggestions:
   - Match participant names using the name resolution algorithm:
     - Check `_contacts/*/_meta.yaml` for `display_name` or `aliases` match
     - Check org config `team[]` for internal team members
     - Matching is case-insensitive with Swedish character folding
   - Identify org context from ops-config team lists
   - Suggest target folder (e.g., `_contacts/david-ekberg/`, `acme/meetings/management/`)
   - See [Contact Metadata Schema](../ops-config/contact-meta-schema.md) for name resolution details

4. **Confirm with user** -- present classification + routing summary:
   ```
   Classification: transcript (high confidence)
   Target skill: /transcript
   Target folder: _contacts/david-ekberg/

   OK to proceed? (or override)
   ```
   Wait for user confirmation or override. Keep it brief -- one question, then go.

5. **Store in `_inbox/`**:
   - Write content to `_inbox/YYMMDD-HHMMSS[-slug].md` with frontmatter per [`docs/schemas/inbox.md`](../../docs/schemas/inbox.md)
   - Set `id`, `created`, `classification`, `confidence`, `status: pending`, and routing fields in frontmatter
   - Rebuild `_inbox.yaml` index from the new frontmatter
   - If the input was paired audio, set `source.audio_path` and ensure the audio is at `_inbox/.audio/<id>.m4a`

6. **Execute the downstream skill automatically:**
   - `transcript` -> Run `/transcript` with the content, passing the target folder
   - `ops` -> Run `/ops` with the content, passing the org config
   - `task` -> Run `/tasks add [extracted description]`
   - `note`/`idea` -> Already saved, no downstream skill needed
   - **Do NOT just print instructions** -- actually invoke the skill and let it run
   - The downstream skill handles all its own processing (summary, changelog, task import, insights, history)

7. **Archive the inbox item** after the downstream skill completes:
   - Set frontmatter `status: processed`, `processed_at` to now (ISO 8601), `processed_by` to the downstream skill name, `output_file` to its output path
   - Move the .md file to `_inbox/.archive/<id>.md` and set `archived_to`
   - If paired audio exists, also move `_inbox/.audio/<id>.m4a` to `_inbox/.archive/.audio/<id>.m4a`
   - Rebuild `_inbox.yaml` to reflect the new state

### `/inbox status` -- Show Inbox State

Display counts by status (per [schema](../../docs/schemas/inbox.md)):
```
Inbox Status:
  pending:    3 items
  processing: 1 item
  processed:  5 items (will archive on next process run)
  skipped:    1 item
  archived:   12 items (in .archive/)
  ---
  Active:     4 items (pending + processing + skipped)
  Orphan audio: 1 file (audio in .audio/ with no matching .md yet)
```

Also show classification breakdown of active items, flag any orphan
audio waiting in `.audio/` for a matching transcript, and flag orphan
**file drops** in `.files/` with no paired stub (offer to register them,
CR-024).

### `/inbox process [id|all]` -- Process Stored Items

Process one item by ID, or all unprocessed items (status `new` or `classified`) with `all`.

**Single item (`/inbox process <id>`):**

1. Read the item's frontmatter and body from `_inbox/<id>.md`
2. If `classification` is missing or `confidence: low`, run classification (Step 2 from default flow); update frontmatter
3. Show classification and target, ask user to confirm or override
4. Set frontmatter `status: processing`
5. **Execute the downstream skill** -- actually run `/transcript`, `/ops`, or `/tasks add` as appropriate
6. After the downstream skill completes, update frontmatter:
   - `status: processed`, `processed_at`, `processed_by`, `output_file`
7. Move `<id>.md` to `_inbox/.archive/<id>.md` (and paired audio to `.archive/.audio/<id>.m4a` if present); set `archived_to`
   - **File drops (CR-024):** the source file in `.files/` moves WITH the output -- to the target folder's `.attachments/` when it should accompany the content (ask if unclear), else to `_inbox/.archive/.files/<id>.<ext>`. Record the final path in the stub before archiving it.
8. Rebuild `_inbox.yaml` from current frontmatter

**All items (`/inbox process all`):**

1. Glob `_inbox/*.md`, read each frontmatter, filter to `status: pending` or `processing`. **Skip working documents** (registered `type: working_doc` / tag `do-not-process` per the [schema](../../docs/schemas/inbox.md) CR-022 section) -- they are surfaces, not captures
2. If no items to process, report "Inbox is empty" and stop
3. Read and classify all items, then present a single confirmation table:
   ```
   | ID                       | Title          | Classification | Target      | Folder              |
   |--------------------------|----------------|----------------|-------------|---------------------|
   | 250428-091803-bob        | samtal med bob | transcript     | /transcript | _contacts/bob/      |
   | 250428-103044-sara       | samtal med sara| transcript     | /transcript | _contacts/sara/     |
   ```
   Ask: "Process all? (or list IDs to skip/override)"
4. **Execute each item sequentially** -- for each confirmed item:
   a. Set frontmatter `status: processing`
   b. Run the downstream skill with the body content and target folder
   c. Let the skill complete its full processing (summary, changelog, tasks, insights)
   d. Update frontmatter: `status: processed`, `processed_at`, `processed_by`, `output_file`, `archived_to`
   e. Move `<id>.md` to `.archive/`; move paired audio (if any) to `.archive/.audio/`
   f. Brief status line: "Item 250428-091803-bob done -- processed as transcript -> _contacts/bob/"
5. Rebuild `_inbox.yaml` once at the end
6. Print final summary: "Processed N items, M archived, K orphan audio still pending"

**Key principle:** `/inbox process` is an automation pipeline, not a suggestion engine. It actually runs the downstream skills. Human-in-the-loop is limited to:
- Confirming the classification table before execution begins
- Any validation questions the downstream skill itself needs (e.g., `/transcript` may ask about participants, `/ops` may ask about org config) -- pass these through to the user as they come up

### `/inbox triage [refresh|status]` -- Triage working-surface upkeep (CR-022)

Operates on the vault's registered **triage doc** (see the
[schema](../../docs/schemas/inbox.md) CR-022 section: an `_inbox/` working
document with `type: working_doc`, `status: keep`, tag `do-not-process`).
If no triage doc is registered, say so and stop -- this subcommand never
creates one uninvited (offer to register an existing file if the user asks).

**Design principle (blocking): mechanical upkeep only.** Never reorder,
reword, re-bucket, or classify items. Sorting INKORG is the human's
thinking step, not the skill's.

**`refresh`** (default):

1. **Week anchor:** update the `Veckoankare:` line's date and week number to
   today. Preserve the standing-context text after them verbatim -- only the
   date/week tokens change. If bucket headings carry week numbers
   (e.g. `DENNA VECKA (v.26)`), update those tokens too.
2. **Archive done items:** move every `[x]` bullet (with its sub-bullets) to
   `_inbox/.archive/YYMMDD-triage-klart.md` -- create with a `# KLART-arkiv`
   header if missing, append under a `## YYMMDD` heading, keep the bullet
   text verbatim plus a `(arkiverad YYMMDD)` suffix.
3. **Report INKORG age:** list INKORG items that have sat unsorted >7 days
   (report only -- sorting is the human's job).
4. **Flag plaintext credentials:** scan for password/API-key-looking lines
   (per the schema's no-secrets rule) and report them with the
   recommendation to replace with a password-manager reference. Never
   auto-remove. **Skip lines ending in `<!-- secret-ok -->`** — that marker
   is the owner's recorded decision to keep the credential inline; offer to
   stamp it when the user says a flagged line should stay.
5. **Report:** one summary line per action class (anchor updated, N items
   archived, M INKORG items aging, K credential flags). Show the diff of
   what moved.

**`status`:** read-only -- items per section, INKORG count + oldest item
age, `[x]` items awaiting archive, week-anchor date vs today, link to the
KLART archive.

**Optional external mirror (CR-017).** A vault MAY point the triage doc at an
external actions surface (e.g. a task app) for a focused *today/tomorrow* view.
This is a **local, per-vault integration** -- its script, credentials, and
target IDs live outside this repo (a local script dir + a gitignored secret;
a config pointer under the vault's own config layer). Two directions, both
**human-gated**, mirroring the mechanical-upkeep principle above:

- **push** -- mirror the current actionable *today/tomorrow* items outward,
  after showing a preview. One-way, additive, deduped against what's already
  there. It never invents dates: an item ships only if the line itself marks
  it today/tomorrow.
- **reconcile** -- read the external surface back and, for items completed
  there, **propose** marking the matching triage line `[x]`; the human
  approves before anything is written. Never an automatic merge -- the triage
  doc stays the system of record, consistent with "sorting is the human's
  step, not the skill's."

This skill documents the *contract* (gated push + gated reconcile, secret-free,
triage-path read from the registered `file:`); it ships **no** provider code.

### `/inbox help` -- Usage Guide

Print this usage guide:

```
/inbox [content]        Capture, classify, and route content
/inbox status           Show inbox counts by status
/inbox process [id|all] Process one or all stored items
/inbox triage [refresh|status]  Triage working-surface upkeep (CR-022)
/inbox help             This help text

Content types detected: voice_memo, email, quick_note, raw_text, clipboard
Classifications: transcript, ops, task, note, idea

Items are stored in vault/_inbox/ and tracked in _inbox.yaml.
After processing with the downstream skill, items are archived.
```

## `_inbox.yaml` -- derived index

`_inbox.yaml` is a derived index, rebuilt from the frontmatter of every
`_inbox/*.md`. It exists for fast batch listing (Marvin, `/inbox status`)
without parsing every markdown file.

The canonical metadata for any single item lives in that item's
frontmatter, not here. If frontmatter and index disagree, frontmatter wins.

`/inbox` rebuilds the index on every `status` or `process` invocation.
External tools writing items directly into `_inbox/<id>.md` MAY also append
to the index, but they are not required to -- the next `/inbox` call picks
up new files from frontmatter automatically.

See [`docs/schemas/inbox.md`](../../docs/schemas/inbox.md) for the index
schema (`version: 2`, post-CR-012). v1 files are detected and rewritten
lazily on first run.

## Content File Format

Markdown with **YAML frontmatter** (canonical metadata) and a body. See
[`docs/schemas/inbox.md`](../../docs/schemas/inbox.md) for the full
frontmatter spec.

Filename: `YYMMDD-HHMMSS[-slug].md` where:
- `YYMMDD-HHMMSS` is creation timestamp (vault-local)
- `slug` is optional, kebab-case, derived from a short title

Minimal valid frontmatter:

```yaml
---
id: 250428-091803
created: 2026-04-28T09:18:03+02:00
status: pending
---
```

`/inbox` populates additional fields (classification, target_skill,
target_folder, confidence) when the item is classified.

## Integration

- **Reads** ops-config for team member recognition and org routing
- **Reads** CLAUDE.md MEETING ROUTING for folder suggestions
- **Reads** `_contacts/` folder structure for participant matching
- **Executes** downstream skills (`/transcript`, `/ops`, `/tasks`) -- not just suggestions
- **Marvin** (formerly core-skills-visualisation) provides a web UI for the same data (create, view, classify, archive)
- **Trillian** (vault-pulse) writes raw audio to `_inbox/.audio/` and frontmatter-only stub markdown to `_inbox/<id>.md`; the matching transcript arrives later from Deep Thought
- **Deep Thought** writes transcript markdown back to `_inbox/<id>.md` with `source.external_id` set to the DT job id

## Archive Policy

Follows the core-skills archive pattern: never delete, always archive.
- Processed markdown moves to `_inbox/.archive/<id>.md`
- Paired audio (if any) moves to `_inbox/.archive/.audio/<id>.m4a`
- The pairing-by-basename rule is preserved across the move
- `_inbox.yaml` retains the item entry with `status: processed` and `archived_to` set
- Archive is browsable but not shown in active views
- Retention for archived audio is user-configurable (default: keep indefinitely; future `/inbox prune --older-than Nd` will reclaim space)

## Rules

- Confirm classification with user before executing -- one brief confirmation, then go
- **Execute downstream skills automatically** after confirmation -- do not just print instructions
- Minimize questions -- classify, confirm, execute, archive. No back-and-forth.
- **Frontmatter is canonical**; `_inbox.yaml` is a derived index. If they disagree, frontmatter wins.
- Use the `YYMMDD-HHMMSS[-slug]` identifier format (per [schema](../../docs/schemas/inbox.md))
- One content file per inbox item; pair with `.audio/<id>.m4a` if audio captured
- Items created via Marvin web UI, Trillian, or Deep Thought follow the same schema
- After downstream skill completes, archive the inbox item automatically (and its paired audio)

