Reviews a finished /orchestrate run and proposes the smallest change to the pipeline docs that would have prevented what went wrong — or says nothing needs changing. --audit reviews the pipeline docs themselves: size, retirement candidates, dead references, review-cycle trend.
Installs into .claude/skills of the current project.
Are you the author of Retro?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/zalaras-retro)
---
name: retro
description: "Reviews a finished /orchestrate run and proposes the smallest change to the pipeline docs that would have prevented what went wrong — or says nothing needs changing. --audit reviews the pipeline docs themselves: size, retirement candidates, dead references, review-cycle trend."
argument-hint: "[plan-name] | --audit"
allowed-tools: Read, Grep, Glob, Bash, Edit, Write
---
> Maintainer note: exists because ad-hoc retros kept growing the pipeline docs.
Run it in the main session, normally at the end of the session that ran `/orchestrate` — the
orchestrator's own stumbles (a mis-stamped state, an improvised wave, a re-sent prompt) live only in
that conversation. It also works later, from the files alone; say so in the header when it does.
Argument: **$ARGUMENTS**
- No argument → the plan is inferred: `plan/<name>` if HEAD is on one, else the
`plans/*/orchestration-state.json` with the latest `updated_at` whose `status` is
`completed`. Name it in the report header.
- `<plan-name>` → that run.
- `--audit` → § 5, nothing else.
## 1. Gather evidence — measured, not remembered
```bash
S=.claude/skills/orchestrate/scripts/orch-state.py
python3 $S <plan> timings && python3 $S <plan> show # wall-clock, retries
ls plans/<plan>/ # review cycles (review.cycle<N>.md), decisions/
git log --format='%h %s' main..plan/<plan> # commit suffixes, who committed what
```
Read every `review*.md` (all cycles), each `## Fix Attempt` in the implementation logs, the
`## Repairs` and `## E2E Implementation Bugs` tables in `test-specs.md`, and any
`decisions/*/decision.md`. Then add what only you saw: every point in this session where you
deviated from `orchestrate/SKILL.md`, re-sent a prompt, or were corrected by the developer.
An incident is a **cost with a cause**: a review cycle spent on something an earlier step
should have caught, a fix that came back, a gate that passed on a broken tree, a step that ran
twice, a rule you had to improvise, **a review `[note]` on a comment the judge kept** (check the
ledger's reason in `plans/<plan>/comment-pass.json` — was the judge's context enough?). Wall-clock
spent doing the work correctly is not an incident.
## 2. Classify each incident against the rule that already exists
Before writing any proposal, run `go run ./tools/kb find <words>` and grep `.claude/skills/`, `.claude/agents/`, every `CLAUDE.md`
(`find . -name CLAUDE.md -not -path '*/node_modules/*'`) and `docs/conventions.md` for the rule or lesson
that governs the incident, and read the whole section it sits in. Root `CLAUDE.md` is loaded into
every session and every subagent, so a line there is the most expensive line in the repo — it
must earn it. Then exactly one of:
| Finding | Proposal |
|---|---|
| A rule exists and was followed; the cost was the pipeline working | No finding. Say so in one line if it looks like one. |
| A rule exists and was **broken** | **Never add a sentence.** First count its edits: `git log --oneline -S'<a distinctive phrase of the rule>' -- <file>`. One commit → sharpen it in place, no more words than before. Two or more → rewording has already failed twice; the only proposals allowed are mechanical — a `plan-lint.sh` / `gates.sh` / `dead-refs.py` / `orch-state.py` check, a verdict field the orchestrator must read — or dropping the sentence. |
| No rule exists | A **lesson record**, not a sentence: `docs/lessons/<slug>.md` with `roles:` (the agents that pay next time), `refs: [plan:<name>, <evidence file>]`, and one sentence of lesson — `kb pack` delivers it to those roles. An agent file changes only for a **rule** (a must/never a gate or the reviewer enforces), in the file the actor reads: agent → its `agents/*.md`; orchestrator → `orchestrate/SKILL.md`; plan shape → `plan-work`; a fact any session in one directory needs → that directory's `CLAUDE.md`, hand-written part. |
| One-off, environmental, or developer preference | No proposal. List it under *Not proposing*. |
**Every proposal states its net word delta** for the file it edits (`wc -w` before → after).
A lesson record has no delta to state; it counts toward the pack-size table in § 5 instead.
Words, not lines — lines were gamed by length. **Whenever a proposal touches a file, also
read that file for sentences whose class a script now enforces** (`plan-lint.sh`, `gates.sh`,
`dead-refs.py`, `e2e-lint.sh`, `orch-state.py`, `.githooks/*`) and list each as a *retire*
proposal naming the covering check — only where the check runs before the reader acts; a rule an
author-agent reads before writing stays, or it learns from a red gate after a wasted run.
Retirement is always a proposal; the developer vets.
### Size thresholds — warnings, never refusals
| File | Warn above | Basis |
|---|---|---|
| `CLAUDE.md` | 150 lines | loaded into every session and subagent; the docs say "keep it short" and prune what Claude already does |
| `.claude/skills/*/SKILL.md` | 500 lines **or** 5,000 words | Claude Code docs: keep SKILL.md under 500 lines, move reference material to supporting files |
| `.claude/agents/*.md` | 500 lines **or** 5,000 words | same as skills; supporting files live in the agent's wrapper skill dir (`.claude/skills/<agent>/`) and are read on demand |
When a proposal would leave a file over its threshold, say so in the proposal and state in one
line why a script cannot carry the lesson instead.
## 3. Report — short, then stop
At most three findings, most expensive first, each citing a file, a cycle or a timing row — never
memory alone. No preamble, no per-step narrative.
```
## Retro: <plan> (inferred | from files only, in-session stumbles unavailable)
<total wall-clock>, <N> review cycle(s), <M> fix wave(s), <K> validate attempt(s).
1. **<what went wrong>** — <evidence: file, cycle, minutes>.
Proposal: <amend | mechanise | add (only where no rule exists) | remove | retire | lesson> `<file, or docs/lessons/<slug>.md roles [r]>` § <section> — <the sentence, check or lesson, verbatim>. (<file>: <w> → <w> words)
Not proposing: <one line each, or "nothing">.
```
If nothing qualifies: `## Retro: <plan>` and one line saying the run had no incident worth a
rule. That is a legitimate result, not a failure to look hard enough.
Then stop. The developer picks by number in prose; ask in prose, not with a menu.
## 4. Apply what the developer picks — and nothing else, retirements included
- Edit only `.claude/skills/**`, `.claude/agents/**`, any `CLAUDE.md` (root or subdirectory,
creating one where step 2 placed it), `docs/conventions.md`, and `docs/lessons/*.md`. After a
record: `make gen-kb && make check-kb`; the regenerated files ride the same commit. Never
`plans/` (the one exception is the § 5 audit file, when the developer asks for one), never product
or test code — a code defect the retro finds is *proposed* in the report, for the developer to file
or drop (kb:adr/process-backlog-entries-are-the-users-to-file); never written into `TODO.md`
and never into this commit.
- Commit on **the branch the run happened on**, as `docs(retro): <plan> — <what changed, one
line>`; it rides that plan's squash merge. Stay on `plan/<plan>` — a lesson may then cite
`plan:<plan>` and the run's own `review.cycle*.md`, which do not exist on `main` until `/land`.
Only when that branch is gone (the plan already landed) commit on `main`, and say so. An
`--audit` pick has no run of its own: commit on the branch checked out where it ran — normally the
plan branch of the session that just finished — as `docs(retro): audit — <what changed>`, and name
the branch in the report. Stage only the files you edited by name — no `git add -A`, no stash — and
leave pushing to the developer.
- Report the commit sha and the measured delta: `wc -w` and `wc -l` of each edited file before
and after, and whether any file is over its threshold.
## 5. `--audit` — the pipeline docs themselves
Run when the developer asks. Read-only: it writes only the report, every line a numbered proposal
the developer picks from (step 4 applies the picks).
```bash
for f in CLAUDE.md .claude/agents/*.md .claude/skills/*/SKILL.md; do
printf '%6d %6d %4d %s\n' "$(wc -l <"$f")" "$(wc -w <"$f")" "$(awk 'length>400' "$f" | wc -l)" "$f"; done # lines words long-lines
python3 .claude/skills/orchestrate/scripts/dead-refs.py --all
make check-kb
P=$(ls -t plans/*/orchestration-state.json | head -1 | xargs dirname | xargs basename)
for r in planner daemon-impl web-impl daemon-tests web-tests e2e-specs review review-browser review-maintainability doc-reconcile orchestrator; do
printf '%6d %s\n' "$(go run ./tools/kb pack --plan "$P" --role $r | wc -w)" "$r"; done # pack sizes
grep -ohE '\b[a-z0-9-]+\b' .claude/agents/*.md .claude/skills/*/SKILL.md | grep -xF -f <(for p in plans/*/; do [ -f "$p/orchestration-state.json" ] && basename "$p"; done) | wc -l # anecdotes: run dirs only
for p in plans/*/; do [ -f "$p/orchestration-state.json" ] && echo "$(ls "$p" | grep -cE '^review(\.cycle[0-9]+)?\.md$') $(basename "$p")"; done | sort -n # review cycles per run (merged files only — parts are review.<part>.md)
```
Report, in this order, each item numbered:
1. **Size** — every file over a threshold, with its numbers and the section that grew most
since the last audit (`git log -p` on the file).
2. **Retirement candidates** — sentences whose class a script now enforces (method as in § 2),
each with file, line, the sentence, and the covering check.
3. **Dead references** — the `dead-refs.py --all` output, or "clean".
4. **Anecdote load** — the count, and the ten longest anecdotes (plan name plus more than a
clause) as *move to a lesson record* proposals (roles = the agent whose file holds it).
5. **Review-cycle trend** — cycles per run in date order; if the last three runs each needed
two or more, name the cycle-one Major class they share.
6. **Pack size** — the table above (each pack now opens with its own count and a per-section
breakdown, so `wc -w` runs ~40 words high and the section that grew is read off the
`kb: sections` line rather than derived); a pack over 20,000 words (`PackWords`, `internal/kb/budget.go`)
warns exactly like an agent file over its threshold (it is read on top of one) and names the record class that grew (`kb ls --role <r>`, by `date`); a
lesson no plan has cited in the last five runs is a *retire* proposal.
The previous audit is `plans/_audit/skills-agents-audit.md`; write the new one
beside it as `plans/_audit/audit-<date>.md` only if the developer asks for a file.