# Revmux

> Run supervised multi-agent reviews with the revmux CLI, report or act on findings, and re-review fixes. Supports branches, commits, paths, fetched pull requests, visible overlay runs, review-fix loops, and issue or discussion triage with grounding, thesis, antithesis, and cost panels. Also analyzes archived rounds in self mode to tune local prompts and answers questions about profiles, lenses, task directories, output, exit codes, and archives. Use when the user says revmux, supervised review, multi-agent review, review with revmux, revmux a branch/commit/PR, show the review, triage an issue, is this worth doing, revmux self, tune revmux, or asks about revmux behavior.

- Skill: `umputun/revmux` (Agent Skill, multi-file: 14 files)
- Install (CLI): `npx skillmds@latest add umputun/revmux`
- Raw SKILL.md: https://api.skillmd.com/api/skills/umputun/revmux/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: umputun (https://skillmd.com/u/umputun)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/umputun/revmux

---


# revmux — supervised multi-agent code review

revmux spawns and supervises parallel `claude --print` and `codex exec` subprocesses, watches each for
stalls, retries what hangs, and returns findings on stdout.

It does no scope detection, no git, no PR fetching, no source modification. This skill does that half.

## Script path resolution

The bundled scripts live in `scripts/` beside this `SKILL.md`. Resolve each one's absolute path from this
skill's `SKILL.md` entry in the available-skills catalogue, and write that path, in double quotes, in place of
the `<the absolute path this session resolved for scripts/…>` placeholder wherever a command below carries it.
Marketplace plugins live in a versioned cache, not `~/.codex/skills/`, so no fixed path can be written here.

| this skill | revmux |
|---|---|
| resolve what is under review | supervise, stagger, retry, degrade |
| run git, gather context | compose and archive prompts |
| write `scope.md`, `goal.md`, `context/` | merge, dedupe, verify |
| choose profile, lenses, flags | return findings on stdout |
| read the JSON, present, fix, re-run | inject prior rounds |

## Activation triggers

- "revmux", "run revmux", "review with revmux"
- "multi-agent review", "supervised review", "parallel agent review"
- "revmux this branch", "revmux the last commit", "revmux the uncommitted changes"
- "revmux pr 123", "revmux this PR", a pull-request URL — the checkout half, `references/pr.md`
- "triage this", "triage issue 123", "is this worth doing", "should we accept this", "should I close
  this", an issue or discussion URL — the panel over a filed item, `references/triage.md`
- "another revmux round", "re-review after fixes"
- "show me", "I want to watch", "run it visible", "in an overlay" — the overlay form in Step 4, which
  puts the TUI on screen. Any of these with a review request means overlay; alone they are not a trigger
- "revmux loop", "loop it", "keep going until clean" — the review-fix loop, `references/loop.md`
- "revmux self", "self-improve revmux", "tune revmux" — the self mode below, which reviews nothing
- questions: "revmux profiles", "what lenses are there", "revmux exit codes"

## Answering questions without running anything

If asked **about** revmux rather than for a review, answer from the references and do not launch a run.

- `references/task-dir.md` — the round's context files, task and run naming
- `references/invocation.md` — flags, profiles, lenses, overlay backends, config precedence
- `references/output.md` — JSON shape, verdicts, exit codes, run archive
- `references/present.md` — the shape of the turn the user answers: order, detail, decision block
- `references/pr.md` — fetching a pull request into a worktree, `--workdir`, cleanup
- `references/triage.md` — the panel over a filed item: what it fetches, its flags, the six answers
- `references/loop.md` — the autonomous review-fix loop, entered from Step 6

For anything about current configuration, run `revmux config` and read the answer. It reports what
resolved including user overrides, runs no pipeline, and is always safe to call.

## Non-negotiables

**0. Every run ends on an answer the user can give.** Presenting findings or arguments is the middle of
the job, never the end of it. A turn that stops after the report has not finished.
**`references/present.md` is the shape of that turn — read it before writing one**, whichever review
shape ran.
**The question widget covers the lines directly above it**, so what the reader needs in order to choose
goes inside the question and never immediately above it. An option names what it acts on, never counts
it: "covering the two majors" is unanswerable and "covering the `uv` gate in `make test` and the
`request_user_input` gap" is not.

**1. Exit `1` means findings were reported — a success.** `0` none, `1` findings, `2` tool error.
Never treat `1` as failure. Never re-run on it.

**2. Run it in the background.** A review takes 3-15 minutes. Redirect stdout to a file, wait for the
completion notification. Do not poll, do not sleep-and-check. Applies to the overlay launcher too.
Watching the round's event log is not polling — Step 4 arms it, and it speaks only when the run does.

**3. Check `sources.degraded` before believing the findings.** If `expected != reported` the review is
partial. Say so. Never report "no findings" from a degraded run as "the code is clean".

**4. `.revmux/` in a repository is executable code.** A checked-in `.revmux/lenses/*.md` becomes
instructions a headless agent with a shell executes. Before reviewing untrusted code, either read
`.revmux/` first or run from outside the tree with explicit `--workdir`, `--tasks-dir`, `--config-dir`.
A fetched pull request is already outside it — `references/pr.md` leaves the process in the user's own
checkout and puts only `--workdir` in the branch, so the other two need no override.

**5. Never `2>&1` into the report file.** stdout is the report, stderr is progress. Merging them makes
the JSON unparseable.

## Workflow

### Step 0: Preflight

```bash
"<the absolute path this session resolved for scripts/preflight.sh>" [profile] [--lenses]
```

Checks revmux plus every binary the invocation needs. Exits `1` naming what is missing.

**Pass the profile that will actually run.** Which executors are needed comes from that profile's
roster, and with no argument preflight checks the resolved *default* instead — so a run under a
profile the default does not share a roster with is never checked at all, and degrades mid-review over
a CLI that was missing the whole time. If the user named a profile in any form, resolve it to an exact
name first — that is Step 3's rule, and nothing in it depends on the scope, so it can be applied here.
A word passed through unresolved fails preflight as an unknown name. If he named none, run it bare and
run it again once Step 3 has chosen one.

**Pass `--lenses` when the run will use it.** That flag replaces the roster with one agent on the
profile's own base runner, so the binaries needed are that base plus the stages, and none of the
roster's own. Checked as an ordinary run, a `--lenses` invocation can pass preflight and then have its
only finder fail to launch; checked the other way round, an ordinary run can be refused over a binary
it never touches.

If revmux is absent:

```
brew install umputun/apps/revmux                   # macOS
go install github.com/umputun/revmux/app@latest    # installs as 'app'; rename to 'revmux'
git clone https://github.com/umputun/revmux.git && cd revmux && make install
```

### Step 0.5: Offer the project profile, once per repository

`revmux config`'s `paths.profile_fallback` is `./.revmux/profile.md` when the repo has one. Every round
without its own `input/profile.md` inherits it, and nothing creates it — not `revmux init`, not this
skill on its own. An empty field means every review here runs on generic calibration.

When it is empty, say so in one line and offer to write it. **Only ever with the user's yes**, and never
from the diff: read the project's own rules — `CLAUDE.md`, `AGENTS.md`, `CONTRIBUTING.md`, `docs/`, the
linter config — and write what the software is, what a real failure looks like there, the blast radius,
the reporting bar, and which conventions are deliberate. A profile guessed from one change is the thing
this file exists to replace.

Offer it once. If he declines, do not ask again in the session, and do not write a round-local
`input/profile.md` instead — that wins over the project file and is exactly the substitution the round
brief forbids.

**Never on a fetched pull request or any tree that is not his**: `.revmux/` is checked-in configuration,
so authoring one there commits a review standard to somebody else's repository.

### Step 1: Resolve what is being reviewed

| the user says | scope |
|---|---|
| nothing, on a feature branch | `git diff <base>...HEAD` |
| nothing, on master with uncommitted work | `git diff` and `git diff --staged` |
| nothing, on master and clean | `git diff HEAD~1` |
| "the last N commits" | `git diff HEAD~N` |
| "since <ref>" | `git diff <ref>..HEAD` |
| "pr 123", "this PR", a PR URL | `references/pr.md` — resolve, fetch into a worktree, review it there |
| "issue 123", "triage this", an issue or discussion URL | `references/triage.md` — the panel over a filed item, not a diff |
| a bare number, kind unnamed | probe pull request, then issue, then discussion — the first that resolves decides which of the two rows above applies |
| a path | that subtree, as a diff plus a read list |

Two commands here, and no more git than this:

```bash
git branch --show-current && git status --short
git diff <range> --shortstat
```

**The shortstat goes into the brief.** It is one line in this session and it is the whole of step 1 for
the subagent, which otherwise spends three to five git calls arriving at the same three numbers — and
they are the numbers Step 4's announcement is built from either way. Everything past the scale is still
the subagent's: the full diff, the file list, what is worth reading.

**Two rows in the table above are not a single range, so the one command does not cover them.**
Uncommitted work on master is `git diff --shortstat` *plus* `git diff --staged --shortstat`, and the
scale handed over is the two added together — a bare `git diff --shortstat` reports nothing at all when
the work is fully staged. A path scope has no range: hand over the file count instead, or hand over
nothing and say so. Do not hand a number the commands in `scope.md` contradict, since the brief makes it
binding — "take the scale as given rather than measuring it again".

**A pull request is a different shape, not a harder ref range.** revmux fetches nothing and checks
nothing out, so a PR has to be on disk before there is anything to review, and the checkout has to be
removed afterwards. Read `references/pr.md` and follow it — it covers steps 1 through 4 for that case
and hands back here at Step 5.

**A filed item is not a change at all**, so there is no range to measure and nothing for Step 2's brief
to read: it reads a diff and returns a shortstat, and a triage has neither. `references/triage.md`
**replaces Step 2 entirely** — it fetches the item, its thread and the author's history into `context/`,
writes the round, and hands back here at Step 5. A bare number goes through the probe in the table
above first; a number that turns out to be a pull request is `pr.md`, whatever the user called it.

Ask only when genuinely ambiguous; a feature branch with uncommitted work is the standard case. Use
`request_user_input` when available, otherwise ask one concise question and stop. **The question is
always this session's, never a subagent's**: a subagent cannot ask, and one that guesses reviews the
wrong thing at full cost.

### Step 2: Prepare the round — in a subagent

Everything between the resolved scope and a round ready to run is delegated: reading the diff,
matching the task, opening the round, and writing its context files. That is a dozen tool calls and
several screens of diff whose output answers nothing the user asked — and it buries the review it is
preparing for.

**It is also the wait the user sits through before the review starts, and the wait is generation.**
Measured over eight archived rounds it ran 65 to 156 seconds, and the wall-clock tracked output tokens
at a flat rate across all eight — so what makes it slow is prose written and turns taken, not tool
latency. The budgets and the prohibitions in the brief are load-bearing for that, not tidiness.

**Expand every path before handing the brief over.** The subagent does not have this skill loaded and
cannot resolve the skill directory itself, so substitute the resolved absolute path into the brief's text,
or its `task-state.sh` step silently runs nothing.

**Say one line first, then spawn it:** `Preparing the round for <what is being reviewed>…` — the
subject in the user's own terms, "the branch against master", "PR 123", "the last 3 commits". Then
nothing further until the subagent returns: a delegated step the user was told about reads as work in
progress, while an unannounced pause reads as a stall.

Spawn **one** subagent with the collaboration tool, without overriding its model or reasoning effort.
Hand it the resolved scope from Step 1 and this brief:

> Prepare a revmux review round. Write files; change no source, run no review, commit nothing.
>
> The scope is: `<the row resolved in Step 1, with its ref range>`, and its scale is
> `<the shortstat Step 1 printed>`. Take the scale as given rather than measuring it again.
> Exclusions the user has stated: `<list them, or "none">` — write these under the scope's
> explicit exclusions. They are decisions already taken, and the diff cannot show you one.
>
> 1. Read the change: the diff for that range, once, and the file list. That is the whole of the
>    exploration. Do not read source files in full, do not grep the tree, and do not run a further diff
>    per directory — naming a file worth reading is the reviewing agent's instruction, not a claim to
>    verify first, and the round exists because four of them are about to read it properly.
> 2. If this session handed you a task id, use it verbatim and go to step 3. Otherwise match an
>    existing task before minting one — a second id for one subject runs as a first round
>    with no history. `revmux config | jq '.paths.tasks'` lists them with `id`, `description`, `url`,
>    `branch`, `base` and `rounds`. Match on `url` or `branch` exactly, failing that on `description`
>    against the subject in hand, and reuse the matched `id` verbatim. Derive one only when nothing
>    matches: `pr-<number>`, `issue-<number>`, a branch name with `/` replaced by `-`,
>    `since-<short-sha>`, or `wip-<branch>`. No path separators, no `..`, no leading dot, not absolute.
> 3. Run `<the absolute path this session resolved for scripts/task-state.sh> <task-id>`. It takes the
>    id as its one argument and exits 1 with a usage line without it. It validates the id and reports the
>    `task.md` anchors, every round, and each round's `input/` state — `prepared` (never reviewed),
>    `claimed` (a review started and never finished) or `ran`. That is the inventory; do not `ls` the
>    task or a round to confirm what it already told you.
> 4. Name the round `NN-label` — `01-initial`, `02-after-fix`, `03-final` — with `NN` one past the
>    highest already there, and do not mix vocabularies across rounds of one task. Then
>    `revmux new --task <id> --run <NN-label>`, exactly as written and with no `--help` run before it.
>    It prints the absolute path of every file to write plus which of them it created.
> 5. Write to those paths and no others. Never join a path, never create a directory the output did
>    not name, and never write over an existing `scope.md` without reading it first.
>    **Every file below is bullets, paths and command blocks — never paragraphs of prose.** Every agent
>    in the roster reads them and then reads the diff itself, so anything the diff already says is length
>    paid for twice, and writing it is most of what this round costs in wall-clock.
>    - `scope` — required, and **under 1500 characters**. What changed, the commands to see it, its
>      scale, which files to read in full, what to ignore. Write commands in plainest form:
>      `git diff master...HEAD`, never `git -c core.pager=cat diff ...` — a leading option defeats the
>      child's permission matching.
>    - `goal` — optional, and **under 1000 characters**. What the change is for, plus a "this is
>      correct only if…" list. **When the round is the last one before the branch merges, make it the
>      gate**: say the round exists to answer whether anything in this diff should not ship, and name
>      what qualifies — a defect in executable code, a change to a prompt, schema or shipped script
>      that would make a later run wrong, or a contradiction that would mislead an agent executing the
>      document. Say that finding nothing is a valid answer, or the round reads as owing findings and
>      manufactures them.
>    - `profile` — **do not write it.** What it holds is about the repository rather than this round, so
>      it lives in `./.revmux/profile.md` and revmux gives every round without a non-empty override the same one
>      with nothing copied forward. You see the diff and the file list, which is not enough to state a repo's conventions,
>      and a generated file here wins over the project's and silently replaces it.
>      Write it only when the user says this subject needs a different bar than the repo's.
>    - `context` — optional. Ticket text, design notes, commit list. Its path is reported but the
>      directory is not created.
>    - `task_file` — when `created` lists it, and also when it is already there but still the unfilled
>      template with every anchor empty. Read it first either way. `description`, plus `url`, `branch`
>      and `base` when known: that front matter is what the next session matches on, and a task.md left
>      as the blank template is what makes the next session mint a duplicate id.
> 6. Stop at the last write. A failed write returns an error, so there is nothing to confirm afterwards
>    — no `ls`, no `diff`, no second `task-state.sh`, no `git status`.
>
> **Do not read the previous round's `scope.md`, `goal.md` or `context/`.** revmux injects every prior
> round into every composed prompt itself, so reading them buys nothing and anchors this round's scope
> on the last one's wording. No round's `profile.md` is read either: the project file revmux resolves is not one of these.
>
> Return JSON only: `{"task": "", "run": "", "round_dir": "", "scope_path": "", "wrote": [],
> "files_changed": 0, "insertions": 0, "deletions": 0, "areas": [], "notes": ""}`. `areas` names the
> parts of the codebase the diff touches. Put anything that went wrong in `notes` and do not paper
> over it.

Read `references/task-dir.md` yourself only if the subagent reports something it could not resolve.

**Nothing else moves into a subagent.** The profile choice, the launch, the report and how it is
presented all stay in this session — they are the decisions the user is waiting on, and a summary of
a decision already made is not the same thing.

**Check what comes back before launching.** A missing `round_dir` or an empty `wrote` means no round
is ready and the run would fail on a missing scope; fix that here rather than launching into it. The
scale numbers are what Step 4's one-line announcement is built from.

### Step 3: Choose profile and flags

| profile | roster | when |
|---|---|---|
| `comprehensive` (default) | `bugs+impl`, `arch+quality`, `docs+tests`, codex peer | real change, real risk |
| `focused` | one `bugs` agent plus codex peer | small or time-boxed |
| `final` | `bugs+impl` plus codex peer, nothing below major | pre-merge |
| `claude-only` | the same four lens splits, all on claude | no codex available |
| `codex-only` | the same four lens splits on codex, and synthesis and verify with them | no claude available |
| `grill-me` | `bugs+impl` and `architecture+quality`, each once on claude and once on codex, all reading against the change | the user wants it torn apart |
| `expert` | two agents at the highest effort, codex `gpt-5.6-sol:xhigh` and claude `fable:xhigh`, each carrying all eight lenses | a plan, or a change nobody wants to get wrong. Slow and expensive; pick it when he says so, not by default |
| `triage` | `facts` (grounding + precedent), `thesis`, `antithesis`, `cost` on codex | a filed item rather than a diff; needs `--no-synthesis --verify-group-by source`, `references/triage.md` |

**Never choose `expert` on your own.** It is two agents at `xhigh` each applying every lens, so it costs
several times what `comprehensive` does, and no property of the subject justifies reaching for it — not a
plan, not a large diff, not a risky one. Pick it only when the user asked for it in words, and say that
is why. Every other profile reviews whatever it is pointed at, a plan included; `expert`'s severity bar
is simply written so a proposal reads as naturally as a diff.

**A profile word is not a profile name.** Map whatever the user said onto the profiles `revmux config`
reports, matching the name first and the `description` second. revmux rejects an unknown `--profile` at
load and never guesses, so resolving is this skill's job — passing his word through unresolved just
fails the run.

| the user says | profile |
|---|---|
| full, everything, deep, thorough, the works | `comprehensive` |
| short, quick, fast, light, small, time-boxed | `focused` |
| last, pre-merge, before merge, strict | `final` |
| claude only, no codex, skip codex | `claude-only` |
| codex only, no claude, codex alone | `codex-only` |
| grill me, tear it apart, be brutal, no mercy, adversarial | `grill-me` |
| expert, best models, highest effort, spare no expense, use sol and fable | `expert` — and only on words like these, never inferred from the subject |
| triage this, is this worth doing, should we accept this, should I close this | `triage`, and the subject is a filed item rather than a diff — `references/triage.md`, which owns the flags it needs |

Examples, not the list. Match on intent — breadth wants `comprehensive`, speed wants `focused`, a merge
gate wants `final` — and read the resolved catalog rather than this table, since a user with his own
profiles has names it does not carry. Name the profile picked and the word it came from. Ask only when
two are genuinely close, as "quick, before I merge" is between `focused` and `final`.

`--lenses a,b` produces **one** agent carrying both lenses and drops the codex peer, losing every
cross-source confidence boost. Prefer a profile unless narrowing is specifically wanted.

Also useful: `--min-confidence=70` for actionable-only, `--no-verify` when a human reads everything.

### Step 4: Run it — headless or overlay

Both return the same report on stdout and the same exit code.

**Headless — the default:**

```bash
revmux --task <id> --run <name> --no-tui > /tmp/revmux-<id>-<run>.json 2> /tmp/revmux-<id>-<run>.log
```

Launch as a yielded command session, keep its session id, and wait on that same session until it exits.

Reviewing a fetched pull request adds `--workdir <worktree>` and changes nothing else — including that
the command is still run from the main checkout, which is what keeps the archive out of the directory
the cleanup deletes. `references/pr.md` has the reasoning.

**Before yielding, tell the user three things** — otherwise they sit for 10+ minutes with no signal:

1. what is running (task, profile, roster size) and the rough duration
2. the stderr log path, and that `tail -f <path>` shows live per-agent progress
3. that they can ask for status any time

On a status request, read the tail of the stderr log and `events.jsonl` in the round directory. Report
the stage, which agents are active, and elapsed. Never guess.

**Then arm the progress feed, before yielding — headless only.** Start a second yielded command session
over the round's own `events.jsonl`:

```bash
tail -n +1 -F <round_dir>/events.jsonl \
  | grep -E --line-buffered '"kind":"(stage|agent_started|agent_done|agent_retried|agent_degraded|dropped|rate_limit)"'
```

Use `write_stdin` to read new output with waits no longer than 60 seconds. When the review session exits,
send Ctrl-C to the tail session because `tail -F` never ends by itself. `references/invocation.md` says
why those seven kinds are used instead of the log.

**Speak at most once a minute, and only about what happened.** Fold every event since the last relay
into one short line — the stage that opened, the agents that started or finished — rather than one
line each: a roster launches four agents within seconds of each other, and four consecutive messages
saying so is the same noise from the other direction. A retry, a degrade or a rate limit goes out when
it arrives instead of waiting for the minute; those change what the user would do next.

**Never report a quiet interval.** No heartbeat, no "no change since the last check", no restating a
milestone already relayed — a feed that speaks when nothing happened is the one the user turns off.
The monitor is woken by the file, so silence costs nothing and needs no announcing.

**The feed belongs to this form alone.** It exists because a headless run says nothing for ten minutes
and the user has no other signal. An overlay run is that signal, on screen, live.

**Overlay — when the user wants to watch:**

```bash
"<the absolute path this session resolved for scripts/launch-revmux.sh>" --task <id> --run <name> [any revmux flag]
```

Detects the terminal (agterm, tmux, zellij, herdr, kitty, wezterm/kaku, cmux, ghostty, iTerm2, Emacs
vterm), runs revmux with its TUI in an overlay, returns the report on stdout. Under agterm: floating
panel at 80% of the pane, or a pane overlay when the session is split, which leaves the sibling pane
live. Either shape is tinted blue. Do not pass `--no-tui`; the script rejects it.

- it blocks for the whole review, so launch it as a yielded command session and wait on that same session
- **no progress feed, and none of the three-things announcement.** The TUI is the progress feed: a
  second tail session here narrates in prose what the user is already watching render, once a minute, and the
  three lines about `tail -f` point at a log he does not need. Say what is running in one line and
  yield.
- **its exit codes: `0`/`1`/`2` are revmux's, `3` is a launcher failure, `127` is revmux not
  installed.** A `3` means no review happened — that is the one to retry.
- overrides: `REVMUX_AGTERM_PERCENT` (80, and forces the floating panel in a split),
  `REVMUX_POPUP_WIDTH`/`HEIGHT` (90%), `REVMUX_AUTO_EXIT`
  (30s; `0` waits for the reader to quit), `REVMUX_TMUX_WINDOW=1` for a disconnect-resilient tmux window

**Choose headless** unless the user asked to watch or is clearly at the terminal.

**If the launcher dies after a run completed, the report is not lost** — it is in the round directory
as `findings.json` and `report.md`. Read from there rather than re-running. A killed run wrote no
report, and most `2`s wrote none either — but the report reaches stdout only after the round is
archived, so a `2` whose stderr names a failure writing it leaves a complete round. Check the round's
`manifest.json` for content before re-running.

`--run` is required. A round that has already run is an error, not an overwrite — but a round whose
review never finished is not one of those, so a retry after exit `2` reuses the same name and the same
`input/`. That holds only while the dead review wrote nothing into the round, which is narrower than it
sounds: the pipeline opens `events.jsonl` as its first act, so anything interrupted after it started is
refused under its own name, and the answer is a new round with the `input/` copied across. It may not
be named `task.md`.

### Step 5: Read the result

Read `references/output.md` for the full shape.

1. **Exit code.** `2` means nothing usable — read the stderr log, which names the cause. If that cause
   is a failure writing the report to stdout, the round is complete and its `findings.json` is on disk;
   read it. Otherwise fix the cause and re-run: the same `--run` while that round holds nothing but its
   `input/`, a new round with the `input/` copied across once revmux refuses the name. `0` and `1` both
   completed.
2. **`sources`.** Non-empty `degraded` means partial; lead with that.
3. **`findings`.** Group by severity.
4. **`open_questions`**, **`pre_existing`** and **`immaterial`** — report each separately.

`sources` holds **agent names** and is the only evidence of independent corroboration. `lenses` holds
lens names and is informational. One agent flagging under two lenses is still one source.

### Step 6: Present, then propose

**Read `references/present.md` now, and write the turn to its code-review skeleton.** It carries the
order of the turn, the one law of detail, the decision block and the rules for the question itself.
The shape of the turn is defined there and nowhere else; what follows here is only what a code review
adds to it.

**Tag every finding with its surface and severity in one bracket before the title:** `[code, minor]`.
The first value is the surface: `code` for executable logic, `comments` for a comment or doc comment
inside a source file, `docs` for a project document, `tests` for test code, or another lowercase word
when none fits (`config`, `build`). revmux returns no surface field, so derive it from `file` and what
the body argues: a `.go` path whose body is about stale godoc is `comments`, not `code`. The second
value is the finding's exact `severity` field. Never omit either value or split them into separate
tags. Format the heading `[surface, severity] Title — file:line, conf N`.

Carry the same combined tags into the counts. "3 findings" says nothing about where the risk is;
"2 [code, major], 1 [docs, minor]" does.

Actionable is `findings` alone — what revmux kept. Report the other three, and keep them out of the
count: `immaterial` is real and judged not worth the fix, `pre_existing` is real and not this change's,
`open_questions` wants a decision rather than an edit.

Recommend the option that fits the outcome:

| the run came back | recommend |
|---|---|
| `sources.degraded` non-empty | re-run the missing source first — a partial review's silence is not evidence, so nothing else is worth deciding yet |
| any `critical` or `major` | fix those, then a new round on the same task — or the loop below, if the user wants it run to the end |
| `minor` only, verdicts not `unverified` | fix now, or note for later — verification weighed each fix's cost against its consequence, so what is left is a question of timing |
| `minor` only, verdicts `unverified` | say nothing was checked, and weigh each one before acting — `--no-verify` drops nothing and produces no `immaterial` list |
| `open_questions` non-empty, no findings | answer them — they are decisions, and the review found nothing to edit |
| nothing actionable | done, and `--profile final` if a merge is next |

Rows are tried in order and the first match wins, so a degraded run outranks whatever it did report.

Ask with `request_user_input` when available, putting the recommendation first and marking it
`(Recommended)`. Otherwise ask one concise question and stop.

**Never start fixing because the findings look clear.** A review produces findings; editing is a
separate decision, and this is where the user makes it.

**Offer the loop as a third way out of that question**, alongside stopping and fixing-then-one-more-round:
review, fix, commit, re-review, until a round comes back with nothing gating. It is for the user's own
branch, never a fetched PR, and it commits without pushing. `references/loop.md` is the whole procedure —
read it when he picks it, not before.

Once he answers with anything other than another round, run the archive check below.

### Step 7: Fix and re-run, if asked

1. Agree which findings to act on. An `immaterial` verdict means revmux already dismissed it — it is in
   its own list, not in `findings`. A `rejected` one appears nowhere at all: the verifier judged it not
   real and dropped it, so only the archive's stage snapshots still hold it.
2. Make the fixes.

**Before committing a fix, do these three checks, in this order.**

**1. Sweep for the shape, not the site.** Name the defect the finding demonstrates as a *construct* —
"a switch over a three-value enum that only tests one end", "a sentence asserting a shape regardless of
the counts", "a phrase that must match a list beside it" — then grep the repo for that construct and fix
every occurrence in the same commit. Grep for the pattern rather than the literal string: a phrase
wrapped across a line break survives a search for the phrase.

This check exists because the other two cannot catch what it catches. Three times in one archived task
a fix landed at the site the finding quoted and left the identical defect elsewhere — once in a file
the same commit was already editing, once in the file beside it, and once **four lines below, inside
the same diff hunk**, in a commit whose own message stated the general rule. Knowing the rule does not
substitute for running the search.

**2. Enumerate what you touched.** Name the input space of the thing you changed and confirm every
member is handled *and* that a test tells them apart:

- an enum or a set of string constants — every value, and every transition between them if direction
  matters
- a struct being folded, copied or serialized — every field, not the ones the change was about
- a platform, a filesystem or an executor the code branches on — each branch
- an error class the code distinguishes — absent, unreadable, malformed, present-but-empty
- a ratio — that the numerator and the denominator are drawn from the same population

The recurring failure is not carelessness on many fronts. It is writing the fix for the case that
prompted it and a test pinning that same case, so the test cannot catch what was left out.

**3. Re-read the finding, then read your fix against it.** Fix the mechanism the finding names, not the
example it happens to use to illustrate it. A finding that says "the switch cannot express a change that
skips `minor`" is not answered by adding one more case to the switch.

Then run the tests and the linter. A fix that breaks the build is not committed.
3. Open the next round on the same task and write its own `scope` — the fixes and the range they land
   in. **That is Step 2's subagent again**, with the same one-line announcement, the task id it
   already returned, every exclusion the earlier rounds carried, and a fresh `--shortstat` for the
   fixes, so a re-review costs the session no more
   output than the first round did — and the brief's step 2 is skipped outright, since the id is not in
   question on a round of a task that already has rounds. Then run it:

```bash
revmux --task <id> --run 02-after-fix --profile <picked> --no-tui \
    > /tmp/revmux-<id>-02-after-fix.json 2> /tmp/revmux-<id>-02-after-fix.log
```

revmux injects the prior rounds itself. **Do not paste prior findings into the scope** — it
duplicates the injection and anchors agents on conclusions they should re-derive.

A pull request re-reviewed after the author pushed is fetched again first, and carries `--workdir`
again: `references/pr.md`, step 6.

**Pick this round's profile from what the fixes touched, not from the round number.** A re-review is
not automatically smaller: round 1's findings may have been fixed by a redesign, and a narrower roster
cannot catch a regression in an area its lenses do not cover.

| the fixes were | profile |
|---|---|
| contained edits inside what round 1 flagged | `final` — `bugs+impl` is the fix-confirmation shape, and it reports nothing below major |
| spilled into tests or structure **the review has not seen** | `comprehensive` — that surface is genuinely new |
| documentation only | `final` — a doc fix is new prose, and `comprehensive` would review it |
| time-boxed, correctness only | `focused` |

**Default to `final` and make `comprehensive` argue for itself.** Across the archived rounds, roughly three quarters of
everything found is `minor` and minors never converge — they arrive at roughly three a round whatever
the gating count is doing, including on rounds where gating had already been zero twice over. `final`
reports nothing below major, and the re-review rounds run under it produced the shortest actionable
lists in the corpus. A fix that touched a test file is not by itself a new surface; the row above wants
a surface the review genuinely has not looked at.

The round-2 failure worth spending a roster on is a fix that does not address the finding, or addresses
it in the wrong place. That is the `impl` lens, which `focused` does not carry — so a re-review narrows
to `final` rather than to `focused`.

Documentation is the one surface a re-review does **not** widen to cover. Every other spill is code the
round before never looked at; a doc fix is a sentence this loop just wrote, and running the `docs` lens
over it produces a finding about that sentence round after round while the code stands still.

Ask with `request_user_input` when available, putting the matching row first and marking it
`(Recommended)`. Otherwise ask one concise question and stop.
Never narrow the roster silently: a user who does not notice gets a smaller review than the one he
thinks he asked for. Skip the question when he named a profile himself.

## Archive housekeeping

Every round keeps each agent's verbatim stream, so the archive grows by roughly half a megabyte per
round and revmux prunes nothing on its own. Check it once a session, after the last round is presented —
never between rounds, and in loop mode only after the loop exits.

```bash
revmux stats
```

`totals.size_mb` is the whole archive. Under **20MB**, say nothing and move on: disk has no place in a
report about findings. Over it, propose getting back to roughly **10MB**.

The proposal is oldest first by `last_run`, whole tasks, and never the task this session reviewed. Each
`tasks` entry carries the `id`, `description`, `rounds`, `size_mb` and `last_run` a choice is made from.

**A task with no `last_run` at all is not the oldest — it is undated**, and sorting it first offers up a
task whose rounds never completed ahead of ones that did. Order those by `id` after the dated ones, and
say in the option that they carry no completed round.

Use one `request_user_input` call before anything is removed. If the tool is unavailable, ask one
concise question and stop:

- the oldest tasks that together get under ~10MB as the first option, marked `(Recommended)`
- the single largest task as a second option, when the first is not already just that task
- keeping everything as the last option

Every option names the megabytes it frees and, for each task it removes, that task's `description` and
`rounds` — an option that says only "clean up old tasks", or names ids without saying what they
reviewed, is not a choice the user can weigh.

**Say what is lost in the question itself:** a removed task takes its rounds with it, so `revmux stats`
and self mode's corpus both shrink by that much evidence.

Then remove exactly what he picked, one call per task:

```bash
revmux cleanup --task <id>
```

It removes that task and prints what went. It is the only thing in revmux that deletes anything, and it
deletes only the task it is named: a path, a round name or a typo is an error that removes nothing, and
a task a running review still holds is refused. Never remove a task directory any other way, and never
widen the set he chose.

## Debugging a review that looks wrong

The archive is the round directory `revmux new` reported, beside the `input/` it was run against:

| question | where |
|---|---|
| why did this agent report nothing? | `agents/<name>.jsonl` |
| did an agent stall or get retried? | `events.jsonl`; a `<name>.retry.jsonl` means it did |
| did synthesis drop something? | `stages/1-found.json` vs `2-synthesized.json` |
| did verify reject wrongly? | `stages/2-synthesized.json` vs `3-verified.json` |
| what was this agent asked? | `prompts/agents/<name>.md` |
| which lens text, from which layer? | `manifest.json` |

## Self mode — tune the configuration from the record

Triggered by "revmux self", "self-improve revmux", "tune revmux". **It reviews nothing**: no round is
opened, no agent is spawned, and the code in the working directory is never read. It reads what past
rounds produced and proposes changes to the review configuration itself.

**Your job is to hand him conclusions an

…(truncated)
