multi-agent review · supervised · auditable

Many agents.
One review standard.

revmux is launched by your coding agent rather than typed by you. The shipped skill works out what is under review, writes the context to disk, runs revmux and reads the report back. A branch, a pull request, a design document or a filed issue all go in the same way, and come back as one structured answer several supervised models have cross-checked.

$ brew install umputun/apps/revmux
revmux · pr-123 / 02-after-fix comprehensive
bugs+impl running 1m40
1
2
3
4
5
6
7
8
9
s
reading app/pipeline/find.go
arch+quality running 1m32
3
4
5
6
7
8
9
0
1
s
checking the roster resolution path
docs+tests retry 0m17
8
9
0
1
2
3
4
5
6
s
stalled 2m0s, relaunched
codex done 2m31s 6 findings, 48210 tokens
synthesis waiting -- 4 sources expected
find synthesis verify

01 / control

Every agent is visible, and every phase recoverable

Each model runs as a subprocess revmux owns, so it can be watched, timed, killed and relaunched. You see what every agent is doing and what it has spent, an agent that goes quiet is restarted without your help, and the whole run is on disk afterwards. A subprocess does not make the model faster; it makes the run something you can hold on to.

watchdog

A stall is caught and relaunched

A finder with nothing on its output for the idle timeout is killed and retried once. If the second attempt fails too, the run carries on without it: the report names the missing source and synthesis is told the real count, so the other agents' work still reaches you. Only a run where every source failed is an error.

standard

One review standard, not one per session

A review assembled ad hoc varies with the prompt and whatever context was left in the session. Here the roster, the lenses, the severity bar and the three stages are files. A contributor gets the review the maintainer would have run, and the maintainer can check which lens text produced it.

roster

The right model for each role

A broad bug pass, an adversarial second opinion, synthesis and verification favour different models, effort levels and costs. Every agent and both stages select a binary, a model and an effort in one model: string, and the resolved runner for every process is recorded.

archive

Auditable by agents, not only by people

Each run keeps the composed prompt every agent received, the verbatim output it returned, the findings after every stage and revmux's own decisions about stalls and retries. A person reads the report. An agent can read the whole run and propose edits to the lens text behind it.

02 / the run

Three fixed stages

Only the roster and the severity bar vary between review shapes. Everything else is configuration.

find
synthesize
verify

--no-synthesis passes findings through with attribution intact. --no-verify marks every finding unverified rather than claiming it was checked.

bugs+impl arch+quality docs+tests codex synthesis verify app/ui verify app/pipeline verify app/task find, in parallel one call verify, by group
stage 01

find

The profile's roster runs in parallel, each agent composing one or more lenses. Launch is staggered: agent one goes first and the rest are released once it produces its first output.

stage 02

synthesize

One model call merges every source, dedupes on (file, line ±2), boosts confidence where distinct sources corroborate, splits out open questions and pre-existing issues, and drops weak singletons.

stage 03

verify

Parallel verifiers grouped by directory, each seeing only its own group so it cannot anchor on a neighbour. Every finding comes back confirmed, refined, rejected, immaterial or pre-existing.

03 / on screen

Watch it, or read the log

A status table with one row per supervised process, a combined chronological pane and a tab per agent, then a findings browser when the report is in. Press i and the same panes show the inputs the round was pointed at.

The revmux terminal UI running four agents, shown as a floating overlay above a Claude Code session
The review, live. The shipped launcher opens the run with its TUI in a terminal overlay, because an agent's shell has no tty. Here it floats above the agterm session that started it, so the session underneath keeps working while the review runs.
A per-agent tab showing the codex agent's own scrollback, with the findings count broken down by severity in the header
Per agent, in full. The header count is broken down by severity and colored by the worst one in it. Each agent keeps its own tab, so when a finding looks wrong you can read what that agent was doing when it raised it.
The input snapshot view showing the round's scope.md rendered as a markdown document
What it was asked. The input snapshot shows this round's scope.md, goal.md, profile.md and every context file, markdown rendered as a document and other text shown as it is, so the context the agents were given is one keystroke away from the run they are doing.

the counting
rule

A source is a process. An agent carrying two lenses that flags the same issue under both is still one source: it cannot corroborate itself.

The cross-source confidence boost counts distinct processes, never lenses and never tags. revmux stamps the attribution itself once the model's output is parsed, and no schema exposes the field to the model, so an agent cannot name itself twice. Codex runs as a peer alongside the lens agents rather than as a second pass, because a reviewer who sees the first reviewer's findings anchors on them and the boost then measures nothing.

04 / context

Review context arrives as a task round

The subject is whatever the scope describes: a branch, a pull request, an implementation plan, a design document, or an issue somebody filed. revmux performs no scope detection, no git operations and no PR fetching, and it modifies no source. The caller writes the round's input/ and passes its name. Variables expand to the paths of those files, never to their contents, so no prompt is bloated by a large scope.

.revmux/tasks/pr-123/        a task: one subject, as many rounds as it takes
├── task.md                 optional anchors: description, url, branch, base
├── 01-initial/             a round
│   ├── input/              caller-written, the only channel context travels through
│   │   ├── scope.md        {{SCOPE}}    required
│   │   ├── goal.md         {{GOAL}}     optional
│   │   ├── profile.md      {{PROFILE}}  optional, the project's own conventions
│   │   └── context/        {{CONTEXT}}  optional, any number of files
│   └── ...                 revmux-written artifacts
└── 02-after-fix/           the next round, with its own input/

Both names are caller-chosen and semantic. A round that has already run is an error rather than an overwrite, because a round that went badly is exactly the one worth keeping. Prior rounds are injected into every composed prompt with their own instruction to re-evaluate independently, so round two knows what round one reported without being told to agree with it.

revmux new
prints every
path you write
to, so nothing
joins one by
hand

open a round and run it
$ scope=$(revmux new --task pr-123 --run 01-initial | jq -r .scope)
$ cat > "$scope" <<'EOF'
Review the changes on this branch against master.
Diff command: git diff master...HEAD
EOF
$ revmux --task pr-123 --run 01-initial > findings.json

16:02:11 bugs+impl     started [bugs, impl]
16:04:02 docs+tests    retrying: agent docs+tests stalled
16:05:12 bugs+impl     done, 6 findings
16:09:03               ── complete ──
16:09:03               6m52s, sources 4/4, degraded none
16:09:03               6 findings: 1 major, 5 minor

05 / the archive

Enough to audit the review without re-running it

Every run writes its artifacts into its own round directory, beside the input/ it was pointed at. One round read in isolation shows both what was reviewed and what came back. A failed archive write fails the run: a report next to a half-written archive reads as complete.

which lens text
raised this?

did synthesis
drop something
real?

.revmux/tasks/pr-123/02-after-fix/
├── input/              what this round was reviewed against
├── manifest.json       roster, prompt provenance and hashes, requested vs actual model
├── prompts/
│   ├── agents/         the composed prompt each agent received, post-substitution
│   └── stages/         synthesis.md, verify-app-pipeline.md, ...
├── stages/             1-found.json, 2-synthesized.json, 3-verified.json
├── events.jsonl        revmux's own decisions: stalls, retries, degrades, transitions
├── agents/             verbatim tees, one file per attempt
└── report.md, findings.json

manifest.json doubles as the marker claiming the round. It is created exclusively as the run starts, which is both how an already-run round is detected and how a real round is told from a directory somebody left behind. Read the archive rules, or turn the corpus into numbers with revmux stats: what each agent and each lens produced, and how many findings the pipeline dropped between stages.

06 / profiles

Any agents, any models, any lenses

A profile is a file. Its front matter names the roster, and its body is the shared preamble and the severity bar. Each entry picks the lenses it carries and, if it wants one, its own binary, model and effort. Synthesis and verification choose separately. Nothing about the shape is fixed, so a review is composed rather than selected from a menu.

.revmux/prompts/
profiles/
release.md

then --profile release

---
description: pre-release pass over the payment path
model: claude/opus:high
agents:
  - {name: money,     lenses: [bugs, impl, payments],    color: red}
  - {name: contracts, lenses: [architecture, docs],      color: cyan}
  - {name: peer,      lenses: [adversarial], model: codex/gpt-5.6-sol:xhigh}
  - {name: second,    lenses: [bugs],        model: codex/gpt-5.6-sol:high}
stages:
  synthesis: claude/opus:high
  verify:    claude/sonnet:low
---

Everything below the front matter is the preamble every agent in this
roster receives, including what counts as critical, major and minor here.

Four sources, two vendors, four different jobs, and a synthesis model stronger than the verifier that follows it. payments is not a shipped lens: it is .revmux/lenses/payments.md, written by whoever knows what goes wrong in that code. A lens is a file of instructions and nothing else, so a project can have as many as it has subjects worth a dedicated pass, and a profile can have as many agents as it is willing to pay for.

eight ship, as starting points rather than as the menu

profile roster
comprehensive bugs+impl, arch+quality, docs+tests on claude, plus an adversarial codex peer. The default.
focused one bugs agent plus the codex peer, for a small or time-boxed change
final bugs+impl plus the codex peer, nothing below major reported
claude-only the same four lens splits on claude, for a machine with no codex
codex-only the same splits on codex, both stages with them, no claude anywhere
grill-me two lens splits run once on claude and once on codex, every agent reading against the change
expert two agents at the highest effort, each carrying all eight code lenses, both stages on fable. For when the answer matters more than the wall clock.
triage a four-way panel over a filed item rather than a diff: grounding, the case for, the case against, and cost

thirteen lenses
ship: eight read
a change, five
read a filed
item

A lens is a file of instructions and nothing else: no executor, no output format, no mention of a prior round. bugs covers correctness, impl goal fit, architecture conventions and organization, quality style and over-engineering, docs and comments the documentation a change leaves stale, tests whether a defect can hide, and adversarial attacks the change looking for what a sympathetic reader would accept.

Prompt text resolves per file across three layers: ./.revmux/, then ~/.config/revmux/, then the embedded defaults. Overriding one lens does not orphan the others, and adding a file is how the set grows. Every lens and key is in the reference.

07 / the blueprint

The review standard lives in the repository

What a project actually cares about, its conventions, what counts as major, the mistakes it keeps repeating, usually lives in a maintainer's head and reaches contributors one review comment at a time. revmux init writes it to disk instead.

commit it, and
every clone
reviews by the
same rules

$ revmux init          # writes whatever currently resolved, ready to edit

.revmux/
├── config                          runtime knobs, shipped commented out
├── lenses/
│   ├── bugs.md                     the shipped lens, now yours to edit
│   └── payments.md                  a lens only this project needs
└── prompts/
    ├── profiles/release.md          the roster above
    └── synthesis.md  verify.md

Checked in, that directory is the project's review, versioned and diffable like the rest of the code. A contributor who clones the repository and runs revmux gets the review the maintainer would have run, before the maintainer ever opens the pull request. A maintainer reading a finding can see which lens text raised it rather than taking it on faith. A review that missed something is fixed by editing a file, once, and every later review has it.

It cuts both ways: that text becomes the instructions a headless agent with a shell executes, so .revmux/ is code and deserves the same look as a Makefile before you run it on a branch you did not write. Running revmux from outside the tree never loads it.

08 / output

Findings to stdout, everything else to the tty

JSON is the default because the usual reader is the agent that launched the run, which decides what to fix, what to argue with and what to put in front of you. --markdown is for when a person is the one reading. The terminal UI renders to the tty and progress lines go to stderr, so revmux --task pr-123 --run 02-after-fix > findings.json works with the display running.

{
  "scope": {"task": "pr-123", "run": "02-after-fix", "scope_path": "..."},
  "sources": {
    "expected": 4, "reported": 3, "degraded": ["docs+tests"],
    "agents": [{"name": "bugs+impl", "lenses": ["bugs", "impl"],
                "executor": "claude", "requested_model": "opus",
                "actual_model": "claude-opus-5", "effort": "high",
                "tokens": 48210, "raised": 6, "degraded": false}]
  },
  "findings": [
    {"id": "f1", "file": "app/pipeline/find.go", "line": 88,
     "severity": "major", "confidence": 90,
     "title": "...", "body": "...", "fix": "...",
     "sources": ["bugs+impl", "codex"], "lenses": ["bugs", "adversarial"],
     "verdict": "confirmed"}
  ],
  "open_questions": [], "pre_existing": [], "immaterial": [],
  "stats": {"duration_ms": 333000, "tokens": 184920, "stages": [...]}
}

exit codes

0 nothing found

1 findings above the confidence floor

2 tool error

1 is a normal outcome. Callers script against these.

sources holds agent names and is the only input to the confidence boost. lenses holds the lens names that raised the finding and is informational: it answers why something was reported, never how many independently agreed. Empty lists are emitted as arrays, so a caller can index into them without a nil check.

09 / the caller

Ask in words, and the skill does the rest

To the agent, revmux is a black box: context in, a verified report out. The skill is what fills that box. It resolves what is being reviewed, runs the git commands, writes the round's input/, launches revmux, reads the JSON back, and opens a new round after fixes. You never compose a command, and the review does not have to be code: a plan, a design document, a proposal or a filed issue goes in the same way.

claude code

Plugin install

/plugin marketplace add umputun/revmux

/plugin install revmux@revmux

Then ask for a review in words: revmux this branch, revmux pr 123, re-review after fixes.

codex cli

Copy the skill

cp -r plugins/codex/skills/revmux ~/.codex/skills/revmux

Same reference material and the same scripts. Both trees carry preflight.sh, task-state.sh, launch-revmux.sh and analyze-corpus.py.

review a change,
or triage what
somebody filed

Triage is the other job. Point the skill at an issue, a proposal or a discussion and it gathers the item, its thread and the author's history into context/, then runs a four-way panel over them: is the claim true of the code as it stands, how were comparable asks decided here before, the strongest honest case for, the strongest case against, and what the work would reach into. The severities rate how much a point bears on the decision rather than what breaks at runtime, and what comes back is arguments for a maintainer to weigh. revmux decides nothing, and neither does the skill: it puts the six questions to you with the case behind each.

Asked for a pull request instead, the skill fetches the head into a throwaway worktree, points --workdir at it while running from the main checkout, and removes both afterwards. An agent's shell has no tty, so launch-revmux.sh opens the run with its TUI in a terminal overlay when you want to watch it happen.

10 / install

Brew it, or take a binary

revmux drives the model CLIs as subprocesses, so whichever ones the profile names must already be installed and authenticated. The shipped preflight.sh answers that for any profile and any invocation.

MIT
no telemetry
no network of
its own

brew install umputun/apps/revmux   # macOS

# linux, from a release: .tar.gz, .deb and .rpm for amd64 and arm64
dpkg -i revmux_<version>_linux_amd64.deb

# or with a go toolchain, installed as `app`
go install github.com/umputun/revmux/app@latest

Full install notes, including building from a clone and how authentication is passed to the model CLIs.