inbox
The chat-native way to clear your roster decision queue. roster review's interactive TUI needs a real terminal; /inbox does the same job conversationally — it lists the decisions, you reply in plain language, and it applies each through the non-interactive roster review backend. Works identically in Claude Code and Codex.
The skill is stateless — it re-reads the queue on every invocation.
Working directory
Operate from the workspace root and classify it before running any command:
- If both
roster.yaml and config/project.yaml exist, stop. This is a mixed workspace; preserve both markers and use the v2 migration flow when #363 lands.
- If
roster.yaml exists, stop with: This is a Roster v2 workspace. /inbox is a legacy queue workflow; do not run roster review or inspect legacy pending/ trees. Portable Brain evidence and Dreamer candidate decisions replace it in #356 and #358.
- Otherwise,
config/project.yaml alone identifies a legacy v1 workspace and the procedure below remains available. A missing roster/ directory just means the legacy queue is empty (roster review --json returns []), not an error.
- If neither identity file exists, stop and say:
Run /inbox from a legacy Roster workspace root (must contain config/project.yaml); Roster v2 workspaces use roster.yaml and do not expose this queue.
(.roster/ — dotted — is scaffold/schedule-spec metadata, not the runtime roster/ queue; never treat one as the other.)
Use that root as <root> for every command below (pass it explicitly with --cwd <root>).
Procedure
List. Run:
roster review --json --cwd <root>
Parse the JSON array. Each entry has id, function, class, filename, path (workspace-relative), target_on_approve (resolved — may be null), frontMatter, and agent on lesson-class items. If the roster CLI is not on PATH, tell the user to install it (npm i -g @firatcand/roster) or run roster review in a terminal, and stop.
The queue spans two surfaces, and --json returns both:
class: "error" — roster/<function>/pending/, synthesized by roster pending sync from failed or stale scheduled fires. The approve target comes from target_on_approve front-matter.
class: "lesson" — <function>/<agent>/pending/ (and <agent>/pending/ for the cross-cutting peers dreamer/ and chief-of-staff/), holding dreamer-drafted lesson candidates. These carry no target_on_approve: approving promotes the file to <agent>/playbook/<filename>, per conventions.md § "Lesson lifecycle". An explicit front-matter target still wins if present.
Empty queue. If the array is empty, reply Inbox zero — no unread decisions. and stop.
Present. For each item, show a compact, numbered block:
N. [<agent or function>] <filename> · id <id> · <class>
on approve → <target_on_approve> (or, when it is null, warn ⚠ no target — can't be approved, only rejected/deferred)
- the first ~6 lines of the body — read it yourself from
<path> (you have Read access; --json returns front-matter only).
Collect decisions. Ask the user what to do, accepting free-form replies like "approve 1 and 3, reject 2, leave the rest." Map each to approve / reject / defer. If the user is unsure, summarize what approve (moves the item to its target_on_approve) vs reject (deletes it) vs defer (leaves it for later) does. For an error-class item (error-<id>.md, synthesized by roster pending sync from immutable failed-fire evidence), reject is the durable skip: roster review --reject also writes an acknowledgement sentinel (pending/acknowledged/error-<id>) so the next sync does not re-create the item from the same evidence — a hand-rm alone would come back.
Apply, one item at a time, using its id:
- approve →
roster review --approve <id> --cwd <root> --json
- reject →
roster review --reject <id> --cwd <root> --json
- defer → do nothing (leave the file).
Parse each result. On ok: true, note the outcome. On ok: false (e.g. missing target_on_approve, target exists, escapes workspace), tell the user the reason and treat the item as still pending (a failed approve changes nothing on disk).
Summarize. Report X approved, Y rejected, Z deferred, and list any that couldn't be applied and why. If anything is still pending, remind the user they can re-run /inbox later.
Rules
- Never move or delete decision files by hand. Always go through
roster review --approve/--reject so the path-safety checks (target must stay inside the workspace, no clobber) are enforced in one place.
- Never edit a decision's body.
/inbox reviews and routes; it does not author.
- One workspace at a time — the root you detected in the working-directory step.
- Identify by
id, fall back to path. The id is roster review's stable handle; if it ever reports an ambiguous id, re-run the apply with the exact workspace-relative path instead.
- The count must match the session-start banner. Both read the same two surfaces (error + lesson). If they ever disagree, that is a bug worth reporting — never work around it by touching decision files directly.
- Do not invoke any model billing path other than the host tool's native subscription, and do not spawn subagents —
/inbox only shells out to roster and converses.
What this skill does NOT do
- Synthesize new decisions (that's
roster pending sync, run by the session-start banner hook).
- Change the count logic or introduce read/seen state — "unread" simply means "still in the queue."
- Run the interactive
roster review TUI (it needs a terminal; this skill is its headless, conversational equivalent).
1---2name: inbox3description: Conversational review of unread decisions in a roster workspace. Lists pending HITL items (roster/<function>/pending/ and <agent>/pending/), shows each in chat, collects approve/reject/defer by reply, and applies each via `roster review --approve/--reject <id>`. No TTY needed — this is the chat-native front door for `roster review`. Triggers on /inbox or when the user asks to review their inbox / pending decisions / HITL items.4---56# inbox78The chat-native way to clear your roster decision queue. `roster review`'s interactive TUI needs a real terminal; `/inbox` does the same job **conversationally** — it lists the decisions, you reply in plain language, and it applies each through the non-interactive `roster review` backend. Works identically in Claude Code and Codex.910The skill is **stateless** — it re-reads the queue on every invocation.1112## Working directory1314Operate from the workspace root and classify it before running any command:1516- If both `roster.yaml` and `config/project.yaml` exist, stop. This is a mixed workspace; preserve both markers and use the v2 migration flow when #363 lands.17- If `roster.yaml` exists, stop with: `This is a Roster v2 workspace. /inbox is a legacy queue workflow; do not run roster review or inspect legacy pending/ trees. Portable Brain evidence and Dreamer candidate decisions replace it in #356 and #358.`18- Otherwise, `config/project.yaml` alone identifies a legacy v1 workspace and the procedure below remains available. A missing `roster/` directory just means the legacy queue is empty (`roster review --json` returns `[]`), **not** an error.19- If neither identity file exists, stop and say:2021> Run /inbox from a legacy Roster workspace root (must contain config/project.yaml); Roster v2 workspaces use roster.yaml and do not expose this queue.2223(`.roster/` — dotted — is scaffold/schedule-spec metadata, not the runtime `roster/` queue; never treat one as the other.)2425Use that root as `<root>` for every command below (pass it explicitly with `--cwd <root>`).2627## Procedure28291. **List.** Run:30 ```31 roster review --json --cwd <root>32 ```33 Parse the JSON array. Each entry has `id`, `function`, `class`, `filename`, `path` (workspace-relative), `target_on_approve` (resolved — may be `null`), `frontMatter`, and `agent` on lesson-class items. If the `roster` CLI is not on PATH, tell the user to install it (`npm i -g @firatcand/roster`) or run `roster review` in a terminal, and stop.3435 The queue spans **two surfaces**, and `--json` returns both:36 - `class: "error"` — `roster/<function>/pending/`, synthesized by `roster pending sync` from failed or stale scheduled fires. The approve target comes from `target_on_approve` front-matter.37 - `class: "lesson"` — `<function>/<agent>/pending/` (and `<agent>/pending/` for the cross-cutting peers `dreamer/` and `chief-of-staff/`), holding dreamer-drafted lesson candidates. These carry no `target_on_approve`: approving promotes the file to `<agent>/playbook/<filename>`, per conventions.md § "Lesson lifecycle". An explicit front-matter target still wins if present.38392. **Empty queue.** If the array is empty, reply `Inbox zero — no unread decisions.` and stop.40413. **Present.** For each item, show a compact, numbered block:42 - `N. [<agent or function>] <filename>` · id `<id>` · `<class>`43 - `on approve → <target_on_approve>` (or, when it is `null`, warn `⚠ no target — can't be approved, only rejected/deferred`)44 - the first ~6 lines of the body — read it yourself from `<path>` (you have Read access; `--json` returns front-matter only).45464. **Collect decisions.** Ask the user what to do, accepting free-form replies like "approve 1 and 3, reject 2, leave the rest." Map each to approve / reject / defer. If the user is unsure, summarize what approve (moves the item to its `target_on_approve`) vs reject (deletes it) vs defer (leaves it for later) does. For an error-class item (`error-<id>.md`, synthesized by `roster pending sync` from immutable failed-fire evidence), **reject is the durable skip**: `roster review --reject` also writes an acknowledgement sentinel (`pending/acknowledged/error-<id>`) so the next sync does not re-create the item from the same evidence — a hand-`rm` alone would come back.47485. **Apply**, one item at a time, using its `id`:49 - approve → `roster review --approve <id> --cwd <root> --json`50 - reject → `roster review --reject <id> --cwd <root> --json`51 - defer → do nothing (leave the file).5253 Parse each result. On `ok: true`, note the outcome. On `ok: false` (e.g. `missing target_on_approve`, target exists, escapes workspace), tell the user the reason and treat the item as still pending (a failed approve changes nothing on disk).54556. **Summarize.** Report `X approved, Y rejected, Z deferred`, and list any that couldn't be applied and why. If anything is still pending, remind the user they can re-run `/inbox` later.5657## Rules5859- **Never move or delete decision files by hand.** Always go through `roster review --approve/--reject` so the path-safety checks (target must stay inside the workspace, no clobber) are enforced in one place.60- **Never edit a decision's body.** `/inbox` reviews and routes; it does not author.61- **One workspace at a time** — the root you detected in the working-directory step.62- **Identify by `id`, fall back to `path`.** The `id` is `roster review`'s stable handle; if it ever reports an ambiguous id, re-run the apply with the exact workspace-relative `path` instead.63- **The count must match the session-start banner.** Both read the same two surfaces (error + lesson). If they ever disagree, that is a bug worth reporting — never work around it by touching decision files directly.64- Do not invoke any model billing path other than the host tool's native subscription, and do not spawn subagents — `/inbox` only shells out to `roster` and converses.6566## What this skill does NOT do6768- Synthesize new decisions (that's `roster pending sync`, run by the session-start banner hook).69- Change the count logic or introduce read/seen state — "unread" simply means "still in the queue."70- Run the interactive `roster review` TUI (it needs a terminal; this skill is its headless, conversational equivalent).