| name | revmux |
| description | 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.
|
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
"<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 " | 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:
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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.