Skip to content
Back to skills

Outbox

ASecurity

Lifecycle management for outgoing material in `_outbox/`. List pending items, close resolved ones into the relevant contact/project folder, and keep manifest, CHANGELOG, and tasks in sync. Use when an outbox item has been sent, replied to, or otherwise resolved -- and the central `_outbox/` should be cleaned up.

  • 4 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added August 31, 2026
businessrustgogit

Security analysis

A100/100

Scanned October 1, 2026

npx -y skills add tandregbg/core-claude-skills --skill outbox --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Outbox?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Outbox
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tandregbg-outbox/badge)](https://www.skillsdirectory.com/skills/tandregbg-outbox)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: outbox
description: Lifecycle management for outgoing material in `_outbox/`. List pending items, close resolved ones into the relevant contact/project folder, and keep manifest, CHANGELOG, and tasks in sync. Use when an outbox item has been sent, replied to, or otherwise resolved -- and the central `_outbox/` should be cleaned up.
user-invocable: true
argument-hint: [list | status | close <folder-name> | close --all-sent | help]
---

# Outbox Skill

Manages the lifecycle of outgoing material staged in `<vault>/_outbox/`. Each subfolder there represents one "send" event (mejl + attachment + preparation). Once sent and resolved, the material belongs in the relevant contact/project folder -- not as eternal residue in the central outbox.

## Vault Location

```
vault/_outbox/
  YYMMDD-<contact-or-project>_<context>/   <- staging while in flight
    _manifest.md                            <- canonical state file
    [files...]
```

**After archiving:**

```
vault/_contacts/<contact>/
  YYMMDD-<context>/                         <- archived inside contact folder
    _manifest.md                            <- updated with Status: arkiverad + Utfall
    [files...]
```

For project-scoped outbox items the destination is the project folder (`<vault>/<venture>/_projects/<project>/` or similar). The skill detects scope from the manifest or asks.

## Manifest schema

`_manifest.md` is the canonical state file for an outbox item. The skill reads/writes these fields.

**The labels below are Swedish because this vault is.** Each one is the written
form of a field whose name is English — `status`, `channel`, `contact`,
`project`, `source` — and a tool that reads manifests should key on the field
and keep the label in a vocabulary file, never as a literal in its code. That
way the same tool serves a vault in another language without a code change.
See `identifier_language` in `ecosystem.yaml`.

```markdown
**Status:** [draft | klar-att-skicka | skickad YYYY-MM-DD | avskriven YYYY-MM-DD | arkiverad YYYY-MM-DD]
**Statusnot:** [free text: what actually happened — channel, time, a link, a circumstance]
**Kanal:** [mejl | slack | print | ...]
**Kontakt:** [email or name]
**Projekt:** [optional theme/context]
**Klassificering:** [team-wide-safe | team-only | management-only]
**Kanonisk källa:** [path to the source doc each attachment is rendered from — or `ingen (originalet bor här)`]

## Innehåll
[file table]

## Skicka
[instructions]

## Svar förväntas på
- [ ] item 1
- [ ] item 2

## Utfall
[populated when resolved]
```

When all `Svar förväntas på` items are checked AND `Utfall` is populated, the item is **resolution-ready** -- ready to archive.

### Editing an existing manifest (CR-102)

`vault_conventions.manifest_edit_in_place`, level invariant. The manifest has two writers -- the
skill that authors it and the dispatcher that records a send -- so it is **edited in place, never
regenerated**:

- Change only the lines you mean to change; carry every other line through verbatim. `Status`,
  `Statusnot`, `Kanal` and `Kontakt` are never dropped, moved out of the field block, or reset to a
  template value by a skill.
- Read the manifest immediately before writing and compare with what you last read. If it changed,
  stop and report instead of writing.
- Past draft (`klar-att-skicka`, `skickad`, `avskriven`), the field block is settled: append to
  `## Utfall` or `## Tidslinje`, and stage a new version of the material as a new item.

Archiving (`close`) moves the folder and appends; it follows the same rule for the lines it does not
mean to change.

### `avskriven` -- resolved without being sent (CR-047)

Some items are resolved by a decision not to send them. A recap that the
meeting made unnecessary, a draft overtaken by events, questions that will be
asked in conversation instead. They were dealt with; they simply never became
a send event.

Before this status existed those items could never be archived, because
`archive` required a sent status -- so they accumulated in `_outbox/`
indefinitely, looking unfinished while actually being done.

```markdown
**Status:** avskriven 2026-09-21
**Statusnot:** Tas muntligt vid nästa samtal -- inget som behöver skickas
```

**Archivable on the same terms as `skickad`, with one addition: `## Utfall`
must say why.** "Not sent, because X" is an outcome, and an item filed without
one is indistinguishable later from an item that was abandoned.

`list` shows these under **RESOLVED, NOT SENT** rather than among the pending,
because a pending list that includes resolved items is a list nobody trusts.

A dispatching surface may set this status (contract `writers` on
`_manifest.md`), for the same reason it may set `skickad`: whether something
was sent is an observation. It must not write the `Utfall` -- what the outcome
*means* is the judgement this skill owns.

#### Superseded drafts -- `ersatt av <item>` (CR-103)

The commonest reason an item is not sent is that a later version replaced it: a draft rewritten
after a decision changed, staged as a new item (CR-102), and sent. The withdrawn item's status note
then says which item replaced it, in one declared form:

```markdown
**Status:** avskriven 2026-09-28
**Statusnot:** ersatt av 260928-contact_offer-revised; first draft, never sent
```

- The status note **starts with** `ersatt av <item-name>` -- the replacing item's outbox folder name,
  optionally in backticks -- optionally followed by `; ` and free text. The logical form is
  `superseded_by <item>`; `ersatt av` is its written label in this vault (`identifier_language`).
- A dispatching surface writes it from its vocabulary file when a person picks the replacing item;
  a person may type it. Anything else in the status note is an ordinary withdrawn reason.
- A superseded draft is **filed differently** from other withdrawn items: to the contact's or
  project's `.archive/`, not beside the correspondence (see `close --all-resolved`). Filed beside
  what was actually sent, a replaced draft reads later as a second message that went out.

### Status words are declared, not invented (CR-103)

When staging (here or in `/ops`), write `status` only as one of the forms in the schema above:
`draft` (or its written form `utkast`), `klar-att-skicka`, and -- written by the surface that did
it -- `skickad`, `avskriven`, `arkiverad` with a date. A free phrase such as "redo att skicka" is
an undeclared status: every tool reading the vocabulary treats it as unknown, and a draft marked
that way looks unfinished to one tool and ready to another. `/ops check` reports undeclared status
words with the closest declared one; it never rewrites them.

### `Klassificering` — who may receive it (CR-066, CR-070)

**Distinct from `Kanal` and `Kontakt`, which say where it is going.** This field says who may
*receive* it, and it is what lets a dispatching surface warn before a send widens the audience of
something never written for it.

| Value | Audience | The test |
|---|---|---|
| `team-wide-safe` | Anyone internal — **the default when absent** | Would it survive being forwarded anywhere inside the organisation? |
| `team-only` | The working team; not interns or contractors | Is the restriction about *employment status*? |
| `management-only` | **Named recipients** | Is there a list of people, rather than a group? |

**Listed most to least permissive, and the order is part of the schema.** The first two scale a
group; the third leaves group distribution altogether. Widening out of `management-only` into any
channel is the step that turns an individual assessment into a group communication — the one a
warning most needs to catch.

**Authored by the skill, read-only to a dispatcher.** A dispatching surface may write `Status`,
`Statusnot`, `Kanal` and `Kontakt`; it does not write this field. It warns and asks for confirmation
when a send would widen beyond the declared value, and does not refuse — refusing would be the
dispatcher deciding what a classification means, which is not its to decide (CR-047).

**Decide it before writing the body, not after.** An item written for everyone and reclassified at
the end is an item whose sentences were composed for the wrong reader.

### `Statusnot` — what happened, in words

`Status` is parsed; `Statusnot` is read by people. The status line answers *which
state is this in*, and a tool matches it with a pattern. The summary answers *what
actually happened* and takes whatever detail makes the event reconstructable
later: the channel and time, a published URL, which of several recipients it
reached, or why a send was partial.

Documented here as of CR-053 because it grew in practice and was in use by 104
of the live vault's manifests while appearing nowhere in this schema. Examples
from those files:

```
**Statusnot:** skickad 2026-08-30 (mejl kl ~12:00)
**Statusnot:** PUBLISHED 2026-09-02 -- https://lnkd.in/p/d6WTTjb5
**Statusnot:** delvis skickat -- hela-teamet-utskicket gick via Teams 26/8 (mejlvägen blockerad)
```

Optional. An item with a plain `Status` and no note is complete.

### `Kanonisk källa` is REQUIRED (CR-032)

Every manifest must state where its material actually lives. Two valid answers:

- **A path** -- the attachment is a *rendering* (PDF out of a `.md`, export out of a dataset).
  The source is the original; the outbox copy is disposable. Example:
  `_contacts/<kontakt>/leverans/YYMMDD-onepager.md`
- **`ingen (originalet bor här)`** -- the material exists nowhere else. Typically the
  `mejl.txt` and the manifest itself. **This is not a defect**, it is a statement that the
  folder must be archived rather than deleted.

**Why it is required.** An audit on 2026-08-28 found 86 outbox items, of which only 3 of 71
manifests named a source. That made a routine question -- *"is this a copy or the original?"* --
unanswerable without opening every folder and grepping the vault. PDFs turned out to be
renderings, but the mejl-texts and manifests existed only in `_outbox/`, so a bulk clean-up
would have destroyed material. The field moves that determination to **creation time**, where
the author knows the answer, instead of to clean-up time, where nobody does.

**Enforcement:**
- `list` flags any manifest missing the field: `(saknar Kanonisk källa)`.
- `archive` **warns but does not abort** -- refusing would strand legacy folders. It asks the
  user to fill it in, and offers `ingen (originalet bor här)` as the default.
- When `/ops` (or anything else) stages new outbox material it must write the field. Leaving it
  blank is the same defect as leaving `Status:` blank.

## SUBCOMMANDS

**Renamed in v1.79.0 (CR-089); `/outbox archive` was removed in v1.81.0.** An old name is not recognised: say that it is now `/outbox close`, and do not run it.

### `list` (default if no args)

**Trigger:** `/outbox` or `/outbox list`

Walk `<vault>/_outbox/*` (excluding `.archive/`), parse each `_manifest.md`, and print a status table:

```
## Outbox status

PENDING (awaiting reply)
  260427-carol-jones_reflektion       skickad 260427      0/2 svar
  260427-dan-smith_reflektion     skickad 260427      0/1 svar

RESOLUTION-READY (archive candidates)
  260427-bob-lindgren_acme      skickad 260427      2/2 svar  Utfall: ja

DRAFT
  260506-someone_topic                 draft               -

RESOLVED, NOT SENT (CR-047, CR-103)
  260428-contact_offer            avskriven 260429    superseded by 260429-contact_offer-revised
  260420-dan-smith_reflektion               avskriven 260421    tas muntligt vid nästa samtal

WITHOUT MANIFEST (manual review needed)
  260418-bob-lindgren_acmecorp    -                   -

MISSING KANONISK KÄLLA (fill in — copy or original?)
  260503-someone_topic            skickad 260503      saknar Kanonisk källa
```

For each resolution-ready item, suggest: `/outbox close <folder-name>`.

If a folder has no `_manifest.md`, flag for manual review -- don't auto-classify.

### `status`

Alias for `list`.

### `close <folder-name>` (was `archive`)

**Trigger:** `/outbox close 260427-bob-lindgren_acme`

**Steps:**

1. **Validate:**
   - Folder exists in `<vault>/_outbox/<folder-name>/`
   - `_manifest.md` exists and parses
   - `Status:` is `skickad ...` or `avskriven ...` (warn if `draft`, abort if missing). An
     `avskriven` item needs `Utfall` to say why (CR-047); when it is empty, write it from the
     status note (`Skickades inte: <note>`, or `Ersatt av <item> (<its status>)` for a
     superseded draft) and show it before moving
   - All `Svar förväntas på` items checked (warn if any unchecked, ask user to confirm)
   - `Utfall` section populated (warn if empty)

2. **Determine destination:**
   - From manifest `Kontakt:` or folder-name prefix, identify contact/project
   - For contact: `<vault>/_contacts/<contact-slug>/`
   - For project: read manifest `Projekt:` or ask user
   - **Multiple contacts** (e.g. ambassador-style fan-out): ask user -- duplicate to each, or pick primary, or keep in a shared `_outbox/.archive/`. Default suggestion: duplicate to each contact folder.

3. **Determine new folder name:**
   - Default: strip contact-name prefix from outbox folder name
     - `260427-bob-lindgren_acme` -> `260427-acme` (or `260427-partnership` if user prefers theme over context)
   - Ask user to confirm or override

4. **Move folder:**
   - `mv <vault>/_outbox/<folder-name> <destination>/<new-folder-name>`
   - Files inside keep their original names (rename only on user request)

5. **Update `_manifest.md`** at new location:
   - `Status:` -> `arkiverad YYYY-MM-DD`
   - Add tidslinje row if not present, and **record where it came from**:
     ``- YYYY-MM-DD: stängd från `_outbox/<folder-name>` ``. A closed item is usually renamed, and
     this line is how a superseded draft closed later still finds the item that replaced it
     (CR-103)
   - Ensure all reference paths (Detaljer:, Source:) point to new locations

6. **Update contact CHANGELOG.md:**
   - Add entry: `**YYMMDD: Ärendet arkiverat** - Outbox-material flyttat till <new-folder-name>/. [link]`

7. **Update `_tasks.yaml`** in contact folder:
   - For any task referencing the old `_outbox/...` path, rewrite the source path to point to new location
   - Don't auto-complete tasks -- that's a separate decision

8. **Update referencing documents:**
   - Search vault for links to old path: `grep -r "_outbox/<folder-name>" <vault>` (excluding `.archive/`)
   - Update each reference to the new path
   - Show diff before applying

9. **Report:**
   ```
   Archived: 260427-bob-lindgren_acme
     -> _contacts/bob-lindgren/260427-partnership/
   Updated:
     - _contacts/bob-lindgren/CHANGELOG.md (+1 entry)
     - _contacts/bob-lindgren/_tasks.yaml (1 source path)
     - _contacts/bob-lindgren/<samtal>.md (1 reference)
   ```

### `close --all-sent` (CR-019; was `archive --all-sent`)

**Trigger:** `/outbox close --all-sent`

Batch mode over the single-folder `close` flow, so a backlog of sent items can be closed in one sitting instead of item by item ( `/ops check` offers this command when it finds sent-but-unarchived items).

1. Run the `list` logic and collect every folder whose manifest `Status:` is `skickad ...`.
2. Present the candidate list up front (folder, destination guess, proposed new name) and let the user confirm all / select / abort.
3. For each confirmed folder, run the standard `close <folder-name>` steps 1-9. Per-folder judgement calls (destination for multi-contact items, folder rename) are still asked individually -- batch mode batches the *selection*, not the decisions.
4. Final report: one summary table (archived → destination), plus the items skipped and why (unchecked "Svar förväntas på", empty Utfall, missing manifest).

### `close --all-resolved` (CR-103; proposed in CR-047)

**Trigger:** `/outbox close --all-resolved`

Batch mode for items resolved **without** being sent (`avskriven`), which `--all-sent` never picks
up and which otherwise accumulate in `_outbox/` looking unfinished.

1. **Plan, deterministically:** run `skills/outbox/resolved.py --vault <vault>` (add `--text` for
   a readable listing). It prints every withdrawn item with its kind (`superseded` or `withdrawn`),
   the replacing item and where it is, the archive sub-path, the outcome line and the timeline line.
   **It moves nothing.** It also lists manifests with undeclared status words.
2. **Show the plan and confirm** all / select / abort, as `--all-sent` does.
3. For each confirmed item:
   - **`withdrawn`** -- run the standard `close <folder-name>` steps 1-9, into the contact or
     project folder, with `Utfall` from the plan when it was empty.
   - **`superseded`** -- resolve the contact or project folder as in step 2 of `close`, then move
     the item to `<that folder>/.archive/<YYMMDD>-<subject>-superseded/` instead of beside the
     correspondence. Write `Utfall` (`Ersatt av <item> (<its status>)`) and set `arkiverad`. Then
     **append** the plan's line to the replacing item's `## Tidslinje`
     (`- YYYY-MM-DD: Ersätter <draft> (avskriven <date>)`), creating the section at the end if it
     is absent -- an append to a settled manifest, which CR-102 permits; its field block is not
     touched. Read that manifest immediately before appending and stop if it changed.
   - **A missing replacing item** is reported by the plan; the draft is closed as an ordinary
     `withdrawn` item and the report says which reference could not be found. Never guess.
4. **Final report:** superseded drafts filed (draft -> `.archive/` path, replacing item), withdrawn
   items closed, items skipped and why, and the undeclared status words found (report only).

### `close --all`

Runs `close --all-sent` and then `close --all-resolved`, with one confirmation per batch.

### `help`

Print this skill's usage.

## Behaviour rules

- **Never delete files.** Only move. Original outbox folder is removed only after successful move (it should be empty). This includes superseded drafts: they are moved to `.archive/`, never removed -- a replaced draft shows what was considered before the version that went out (CR-103).
- **Never auto-complete tasks.** Archiving is a file operation, not a workflow decision.
- **Always confirm folder rename** -- destination folder name is a judgement call (theme vs context vs date-only).
- **Preserve manifest history.** Append to `## Tidslinje` if it exists, never overwrite.
- **Swedish text** must use correct å, ä, ö (vault-wide rule). When generating manifest updates or CHANGELOG entries in Swedish, verify each common word: för, är, på, från, även, över, första.
- **Email .txt files are plain text** -- never reformat to markdown when touching them.

## Manifest detection edge cases

- **No manifest:** flag in `list`, refuse to archive without one. User must create manifest manually first.
- **Manifest with `Projekt:` set, no contact:** treat as project-scoped; archive to `<vault>/_projects/<projekt>/` or venture project folder.
- **Multiple contacts (ambassador case):** flag as fan-out; ask user for resolution strategy.
- **Old outbox layout (`260427-name_topic`)** vs new (`260427-topic`): support both for `list`; new naming is for archived destinations.

## Naming convention for staged folders

`YYMMDD-<recipient>_<subject>/` — the underscore splits **who** from **what**.

**The right side names the subject of the send, not the artifact inside it.** A staged item is a
*folder*: it holds a `_manifest.md` and one or more files, and `_manifest.md` already enumerates them.
Naming the folder after one of its files is a claim that stops being true the moment a second file
arrives — an attachment, a deck, a second cut for a different audience.

| Prefer | Not |
|--------|-----|
| `260921-team-a_standup` | `260921-team-a_standup-recap` |
| `260921-team-c_design-review` | `260921-team-c_design-review-recap` |
| `260916-team-e_topic-with-three-parts` | `260916-team-e_topic-with-three-parts-preread` |

**The exception is when the artifact type *is* the subject** — a board deck sent as a deck, a pre-read
sent as a pre-read, where the recipient asked for that thing and nothing else is coming. `_deck-v2` and
`_preread` are legitimate; `_recap` almost never is, because a recap is what the file is, not what the
send is about.

**The left side names the recipient, not the project.** `team-a_standup`, not
`team-a-v3_standup` — the project is visible from the content and from `_manifest.md`'s `Projekt:`
field, while who it goes to is the one thing a folder listing cannot otherwise tell you.

*(Introduced 2026-09-21 after three projects in one vault staged the same artifact under three
different shapes, and a fourth named its folder after a file it no longer contained.)*

## Naming convention for archived folders

When archiving into a contact's folder, the contact-name prefix is redundant. Strip it:

| Outbox name | Archived name (in contact folder) |
|-------------|-----------------------------------|
| `260427-bob-lindgren_acme` | `260427-acme` or `260427-partnership` |
| `260427-dan-smith_reflektion` | `260427-reflektion` |
| `260427-carol-jones_reflektion` | `260427-reflektion` |

The skill suggests a default but always asks before renaming. For ambassadörs-style (multi-contact) the original name may be retained when archiving to a shared location.

## Integration with other skills

- **`/ops`** -- when ops-skill creates outbox material (preparation, mejl), it stages to `_outbox/`. This skill handles the back end of that flow.
- **`/transcript`** -- transcripts of follow-up calls/replies that resolve an outbox item should reference the resolution doc, which `/outbox close` then links to in the manifest's `Utfall` section.
- **`/inbox`** -- mirror skill for incoming material. Same lifecycle pattern.
- **`/tasks`** -- tasks generated from an outbox item live in the contact's `_tasks.yaml` and survive archiving (paths rewritten).

## Not to be confused with `/handoff` (CR-033)

An outbox item is **addressed to a person** and has a send event, an expected reply, and a resolution. A handoff snapshot is addressed to a **future work session**, is never sent, and is resolved only when a human opens it and starts new work.

If material is going to someone, it is outbox. If it is bounded context for different work, it is `/handoff`. This skill never reads or writes `.handoff/`.

## Out of scope

- Drafting outbox content -- that's `/ops` or manual.
- Sending email -- that's done outside the vault by the user.
- Categorisation/classification -- the manifest's `Projekt:` and folder-name prefix carry the signal.

---

*Created: 2026-05-06*

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.