# Retro

> Run a safeword retrospective on the current session on demand — pull friction (bugs / rough edges / gaps) out of the session transcript and file it upstream through the outbound safety check. Use when the user says "run a retro", "/retro", "retrospective on this session", or wants to capture friction before the session ends. The retro also auto-fires at Stop; this is the manual, on-demand path.

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

---


# Retro

Mine THIS session's transcript for safeword friction and file it — the manual counterpart
to the Stop-hook auto-trigger. This skill is thin: it resolves the transcript path (which
a user-invoked command, unlike the Stop hook, does not receive in a payload), then defers
to the existing guides and the `safeword retro run` CLI, which owns extraction, the egress
guard, and filing.

## 1. Resolve the transcript path (never guess it)

`safeword retro run` **requires** `--transcript <path>` and refuses to guess. Resolve the
current session's transcript for THIS harness. Each rule below resolves deterministically
from the environment; only fall back to asking the user when the deterministic path
comes up empty.

- **Claude Code:** `~/.claude/projects/<encoded-cwd>/$CLAUDE_SESSION_ID.jsonl` — the cwd
  is encoded with non-alphanumerics (`/`, `.`, `_`, …) → `-`. When `$CLAUDE_SESSION_ID`
  is unset, list that projects directory and take the newest `.jsonl`:

  ```bash
  ls -t ~/.claude/projects/"${PWD//[^a-zA-Z0-9]/-}"/*.jsonl | head -1
  ```

- **Codex:** Codex does not reliably expose the session id to agent-launched commands
  ([openai/codex#8923](https://github.com/openai/codex/issues/8923)), so resolve the
  **newest rollout by mtime** — the current
  session's rollout is the one being appended to right now. Rollouts live under
  `${CODEX_HOME:-$HOME/.codex}/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl`
  (`-exec ls -t {} +` is portable to macOS's BSD `find`, unlike `-printf`):

  ```bash
  find "${CODEX_HOME:-$HOME/.codex}/sessions" -name 'rollout-*.jsonl' -exec ls -t {} + 2> /dev/null | head -1
  ```

  If `$CODEX_THREAD_ID` happens to be set, prefer it for an exact match:
  `find "${CODEX_HOME:-$HOME/.codex}/sessions" -name "rollout-*-$CODEX_THREAD_ID.jsonl" 2>/dev/null | head -1`.

- **Cursor:** Cursor delivers `transcript_path` only in hook payloads (never env), so the
  safeword Cursor hooks stash it to `/tmp/safeword-cursor-transcript-<key>` on every edit
  and shell command. `/retro` itself runs a shell command, so THIS conversation's stash is
  the freshest — read the newest one:

  ```bash
  f=$(ls -t /tmp/safeword-cursor-transcript-* 2> /dev/null | head -1)
  transcript=$([ -n "$f" ] && cat "$f")
  key=${f##*/safeword-cursor-transcript-}
  session=$([ -n "$key" ] && cat "/tmp/safeword-cursor-conversation-$key" 2> /dev/null)
  ```

  If either stash is absent (a session with no edits or shell commands yet), **ask the
  user for the transcript path or conversation id** rather than guessing. Public delivery
  proceeds only when the CLI can bind that transcript and conversation to the current
  project using the paired hook state; a mismatch silently keeps the existing private
  recovery path.

Echo the resolved path back to the user before proceeding, so a wrong path is caught
before anything is filed.

## 2. Produce findings — pick one

- **Hands-off (Claude only):** let the CLI extract out-of-band via an isolated headless
  session. This spawns a `claude -p` subprocess, so it needs the `claude` CLI on PATH —
  on **Codex / Cursor** (no `claude` binary) prefer the in-context path below.

  ```bash
  safeword retro run --transcript < path > --auto-extract
  ```

- **In-context (any harness):** extract the friction yourself following
  the packaged retro procedure (fresh-context read of the transcript → structured findings
  JSON), then hand them off:

  ```bash
  safeword retro run --public-retro --transcript <path> --findings <findings.json> --session-id <session-id>
  ```

  On Cursor, use the paired `transcript` and `session` values resolved above. Claude and
  Codex may omit `--public-retro` when no stable session identity is available.

Optional: `--session-id <id>` for stable ledger attribution across fires;
`--window-start <chars>` to digest only the transcript from an offset (delta mode).

When the `safeword` binary isn't on PATH, substitute `bunx --bun safeword@latest`.
This carrier is independent of the project's dependencies, so retro remains available
while Safeword is asking the user to refresh a stale project toolchain.

## 3. Filing is code-owned — do not hand-write issues

Everything flows through the egress guard, then files upstream:

- **Lane 1 (token available):** the CLI files via REST and drains the spool, silently.
- **Lane 2 (no token / cloud):** the CLI spools the sanitized, post-egress drafts and a
  nudge surfaces them; file each **verbatim** through the supported filing route
  using your GitHub access. Never re-word or re-sanitize a spooled draft — the body is
  already code-assembled and egress-clean; the sanitizer is the security boundary.
  Enforced at the code-owned seams: each body is sealed with a `bodyDigest` at
  assembly, and `verifyDraftBody` (`hooks/lib/retro-draft-spool.ts`) refuses a
  mismatch — a re-worded draft stays spooled instead of filing.

## Reminders

- The transcript is the input; the CLI + egress guard do the sanitizing and filing. Keep
  this skill thin — don't reimplement extraction or sanitization here.
- Over-redaction is the safe direction (findings file to a PUBLIC tracker); trust the
  guard, don't loosen it.
- If nothing substantial surfaced, say so — filing zero findings is a valid outcome.

