relevo / docs

relevo docs

Every verb, file and state relevo has, in the order a round meets them: the round, actors and runners, remote builders, the MasterMind, what relevo shows you, the daemon, between rounds, candidates and gates, the refusals, install.

First round

On a clean machine, relevo config init comes first: it writes the candidates, policy and actors sections from the harnesses on PATH and installs the agent definitions relevo ships. relevo doctor then preflights the plugin, the daemon, each harness binary and the roles, and names anything still missing together with the literal fix command.

relevo config init
  # candidates, policy and actors
relevo doctor
  # names what is missing and prints the fix

Then, from the MasterMind session, in the repository you want worked on:

relevo bind --name api --no-feature
  # start a builder on this tree
relevo send --name api --file plan.md
  # hand it the plan
relevo wait --name api --timeout 10m
  # 0 closed · 3 needs you · 5 halted
relevo done api
  # stop relaying

The builder writes NNN-report.md when it has finished and then creates an empty NNN-done as its last action. relevo closes the round on that marker: the marker is the contract, not the report. A runner that exits with a report and no marker still closes the round, flagged unmarked; one that leaves no report is noted noreport. Nothing is read off a terminal and nothing is guessed.

relevo makes no judgement about what came back. When the report lands, run the project’s own check yourself and compare the diff against the plan before calling the round done. In Claude Code the wait runs as a background command and the session wakes with the report — see how reports arrive.

demonstrationauthored from relevo’s output formats · not a capture of a session

$ relevo send --name api --file plan.md --dry-run
would send round 5 to api
  runner    headless agy/google/gemini-3.8-flash-high
  where     /usr/bin/agy -p
  tier      edit
  prompt    /home/me/.local/state/relevo/api/005-prompt.md  (staged from ./plan.md, 4.1 KiB)
  report    /home/me/.local/state/relevo/api/005-report.md
  marker    /home/me/.local/state/relevo/api/005-done
  head      relevo: round 5 · to runner "api" · from the MasterMind (not the human)
            Your working tree is: /home/me/.worktrees/api

The round

relevo wait exits with meaning, so a shell can branch on it:

0
closed on the marker, stdout is the report path
2
closed without it (unmarked, or noreport — verify before trusting)
3
needs you, stdout says what it is waiting on
4
the binding is DONE or was unbound
5
closed, but the report says halted or blocked — read it before sending again
6
the round was never sent, so nothing is in flight
124
the --timeout elapsed.

On every exit but 4 and 124 the wait prints the pending report itself, marked delivered, so collecting a report never means typing into your session.

The report block. A runner ends its report with a fenced relevo block: status, one of done, halted, blocked or deferred; then halted_at, changed_paths, commands_run and not_done. relevo reads the block for its shape, never for meaning: a halted or blocked status is what puts relevo wait on exit 5. A report with no usable block closes the round as unstructured, without error.

Every mutating verb ends by listing, on stderr, every other binding that is waiting on a human — how long, and the verb that resolves it. The exit code is unchanged; it is a reminder, not a refusal.

What a round leaves on disk

An open round’s files sit under ~/.local/state/relevo/<name>/, the state root resolving through $XDG_STATE_HOME. When the round closes, relevo seals those files into relevo.db in the same transaction that closes the round, and removes them, so the directory afterwards holds only the files of a round still open. relevo show reads either: a live binding’s open round, or anything sealed or archived.

NNN-prompt.md
The round’s plan, as the MasterMind handed it over. It opens with an origin line: relevo: round N · to runner "api" · from the MasterMind (not the human).
NNN-report.md
The runner’s report, read back by the MasterMind. Instruction-shaped lines in it are flagged for the reader to see — never altered, never held back.
NNN-done
Empty. Its existence is the completion contract.
NNN-runner.jsonl
The harness’s own stream, one JSON event per line, plus the supervisor’s exit trailer. relevo show --transcript renders it for a human. When the harness’s stream carries times, each rendered line opens with the event’s clock and how long the step took (12:41:03 +4.2s ● Bash go test ./...); codex’s stream carries no time field and is never stamped.
NNN-diff.patch
The round’s captured patch. relevo show --diff prints it, --stat the summary, --drift what changed between rounds, and --anchors each hunk and line with its path:line.
NNN-question.md
A blocked runner’s dialog, captured.
NNN-gate.log
The gate command’s own output, when the round runs one.

The binding itself, its round log and the configuration are rows in relevo.db, not files. Nothing else in the state root is a source of truth.

Actors and runners

Three words, kept apart. An actor is a role — builder, planner, reviewer. A candidate is one way to fill it, named harness/provider/model; each actor holds an ordered candidate list (see candidates, actors, gates). An actor entry also names a placement — where its rounds run, most preferred first; local is this machine and every other entry names a servers entry; absent or empty means ["local"]; bind probes the list in order and --server/--local override it. The runner is the process relevo starts for the round.

One fresh process per round. A runner is the harness’s non-interactive form — claude -p …, agy -p …, opencode run …, codex exec … — started in the binding’s tree with the round’s prompt, writing its own stream to NNN-runner.jsonl and returning when it is done. The daemon renders that stream as it grows, so relevo status and relevo ui show the round live, about two seconds behind. It also means no memory across rounds: a plan must carry its own context.

Reader actors

A reader runs like a builder — bind it, send it a plan — but its round reads and reports instead of writing. relevo runs it in a throwaway scratch copy of the tree: it never edits the shared tree, and nothing it does is committed. Its final message becomes the round’s output, NNN-<actor>/<label>.md, and is queued to the MasterMind like any report.

relevo bind --name webshop --no-feature --actor reviewer
relevo send --name webshop --file q.md
  # q.md becomes the round's prompt
relevo show webshop --output
  # the round's output file

The reader actors relevo ships are planner, lite-planner, reviewer and researcher. The output label follows the actor: a reviewer writes findings.md, a researcher notes.md, a planner plan.md. relevo show <name> --output prints it, and --artifacts lists the round’s other files.

relevo also ships security-reviewer, a second read-only consult that audits a diff, area or question for exploitable issues and returns findings with severity, file:line evidence and preconditions. relevo does not run it automatically — add a security actor whose candidate list names the models, then bind --actor security; the definition supports the two-round protocol (round one to one model, its findings to a second model to confirm or refute each and hunt what the first missed), and it writes findings.md like reviewer.

Planner actors

A planner is a reader actor too: bind a lite-planner, send it a seed, and read its plan back from the round’s output.

relevo bind --name plan-x --no-feature --actor lite-planner
relevo send --name plan-x --file task.md
  # the seed, at most 4 KiB
relevo show plan-x --output
  # plan-x's plan.md
relevo send --name api --file ./plan.md
  # the plan you reviewed, on to a builder

A planner round takes a seed, not a plan: the prompt is capped at 4 KiB and a larger one is refused unless --force. Review the plan with relevo show --output, then hand that output path to a builder. Reviewing is a human or MasterMind step — relevo sends nothing automatically.

Several builders at once. relevo bind gives the MasterMind one builder over the current tree. relevo bind --worktree attaches another, on its own git worktree, so exactly one agent writes to any working tree. Each peer is an ordinary binding: its own round counter, round log, captured diffs and budget, and its own name for every verb that takes one.

Remote builders

A remote binding is an ordinary binding whose builder runs on someone else’s machine, over a signed, pinned HTTPS connection instead of a local process. It has no worktree of its own: relevo send ships a bundle of your branch’s history alongside the plan, and the daemon polls the server for the round’s state the same way it polls a local builder. Nothing else about the round changes — plan out, report back, the marker closes it.

One remote round: the plan and a git bundle go out over pinned HTTPS; the closed round comes back as a fast-forward onto relevo/api. your machine MasterMindyou talk to it, as always relevo CLIbind --server zensend · wait · done relevo daemonpolls the server for the round. refs/heads/relevo/api relevo serve host · zen HTTPS listenerenrolled clients only. serve daemonruns headless builders.no MasterMind on the host. buildera fresh process per round. worktree on relevo/api 001-plan.md + a bundle of relevo/api pinned sha256 fingerprint ed25519-signed client the closed round, fast-forward only
  1. outbound. relevo send ships 001-plan.md and a bundle of relevo/api to the server, over pinned HTTPS.
  2. The serve daemon runs a fresh headless builder in a worktree on relevo/api. No MasterMind there.
  3. inbound. The closed round comes back as a fast-forward onto your own refs/heads/relevo/api.
  4. Your daemon polls the server for the round’s state; status and wait sync first, so a laptop closed overnight still collects the round.
Same two edge styles as the map above: solid is what the MasterMind’s turn sends; dashed is what arrives after it has ended. The wire changes nothing about who decides.

Set up once, per machine

01
On the server host: relevo serve init --host <hostname> once — a server private key and a self-signed certificate — then share the SHA-256 fingerprint it prints.
02
Each client runs relevo config server key: this machine’s remote-builder identity, an ed25519 keypair, and the enrollment line to hand the server’s admin.
03
The admin runs relevo serve enroll --label <label> --key "<line>" on the server, one line per client. Nothing is enrolled until this happens.
04
The client records the server and pins its certificate: relevo config server add zen https://… --fingerprint sha256:… — and, having a fingerprint to check it against, checks enrollment at once.
05
relevo config server list — every configured server and this client’s standing on each: enrolled as <label>, not enrolled, unreachable, cert changed.

This release binds every request signature to the destination server’s identity. A client that pins the fingerprint signs for it and needs nothing; a client that trusts the system CA (--ca system) or runs over plain HTTP (--insecure) signs for the URL’s host, so the server admin starts relevo serve --public-host <that host> (repeatable; the port is dropped), and a server with no accepted audience refuses every signed request. The two ends upgrade together: a pre-upgrade client against an upgraded server gets HTTP 426, and relevo config server list marks that server with server rejected protocol version (426); an upgraded client against a pre-upgrade server prints server predates audience-bound signatures; upgrade relevo on the server. Upgrade both, then enrolled as <label>. A bind --server overrides the actor’s placement, a plain bind follows the placement list, and --local pins this machine.

Then, from any repository

relevo bind --name api --server zen --no-feature
  # the binding, and the local branch relevo/api
relevo send --name api --file plan.md
  # ships plan.md and a bundle of relevo/api
relevo status
  # round state comes from the server, polled

What comes back

The closed round is fetched into your repository’s own refs/heads/relevo/<name> — fast-forward only, exactly like a local builder’s worktree branch. Uncommitted changes on the server land on a side ref, refs/relevo/<name>/round-<N>, and the report names it. Every status and every wait syncs remote bindings first, without needing the daemon, so a laptop closed overnight still collects the finished round on the next relevo status.

What is refused, and what the server is not

relevo serve runs no MasterMind and takes no administrative verbs over the network: administration happens on the server host. A remote builder cannot be changed in place — bind --resume --rebind against one is refused, the same rule a local binding follows; unbind and create it again. Tenants are protected from each other over the wire and from a passive network — not from the server’s admin, and not from each other at the OS level, where every builder runs as the same unix user.

The MasterMind

The MasterMind is the session you drive — the Claude Code or opencode session you run relevo bind and relevo send from. relevo does not pick its harness and does not start it. What relevo provides is the definition, so the same architect runs on any kind; relevo config agents --agent architect installs it, and the harness’s own flag starts it:

claude   --agent architect --model opus
opencode --agent architect -m openrouter/deepseek/deepseek-v4-pro

The plugin

A Claude Code MasterMind installs relevo as a plugin. The plugin carries relevo mcp — the MCP server that exposes status, send, show, gate and done as tools — and a SessionStart hook that runs relevo mastermind init, so relevo knows which session is calling. It also carries the slash commands /relevo:status and /relevo:show beside the consent commands, and the planner-loop skill that walks the send → wait → read → check → done loop. The plugin ships with relevo’s releases.

/plugin marketplace add fuad-daoud/relevo
/plugin install relevo@relevo

Claude Code caches an installed plugin by version, so after upgrading relevo, update the plugin to match — relevo doctor warns when the two differ:

claude plugin marketplace update relevo && claude plugin update relevo@relevo

relevo registers a session as a MasterMind only with your consent, and the slash commands answer for you: /relevo:enable says yes for this session, /relevo:enable-repo yes for this repository from now on, /relevo:disable no for this session, /relevo:disable-repo never in this repository, and /relevo:reset asks the question again. They run relevo mastermind enable, enable --repo, disable, disable --repo and reset. relevo mastermind list shows the records, and relevo mastermind rename <id|name> <new-name> gives one a name of your own. A round’s process is not a consent candidate: it runs with exactly one RELEVO_RUNNER=<binding name> and relevo strips its own identity variables, so a builder or reader session is never asked the repository consent question, never briefed and never registered as a MasterMind.

How reports arrive

The background wait is the default. After each relevo send, the MasterMind runs relevo wait --name <n> --timeout <budget> as a background command and ends its turn. Claude Code wakes the session when the command exits, with the report already in its output; an agy MasterMind is woken by the report itself, through credentials relevo refreshes on every command. Nothing has to be typed into the session.

The channel is opt-in. With the channel enabled, reports and consult answers arrive as channel events the moment the daemon has them, instead of being fetched by the wait. It is off unless you turn it on; a relevo mcp server with no channel runs in tools mode, which is the background wait described above.

A report therefore reaches the MasterMind by exactly one of four routes: the background relevo wait, the channel, a deliverer for a harness that has one, or a relevo wait you run by hand. Nothing is ever typed into a terminal. No verb has to guess who is calling, because the plugin’s hook exports RELEVO_MASTERMIND.

What relevo shows you

Three read surfaces, all text. relevo reports state and moves files; it never summarises a report or grades a round. Every specimen on this page is authored from relevo’s own output formats and labelled so — there is no capture of a real session here.

relevo status — one row per binding, attention first

demonstrationauthored from relevo’s output formats · not a capture of a session

$ relevo status
client   /home/me/src/shop                        round 2   NEEDS YOU  stale 23m
  MasterMind  architect-14    claude   route wait
  runner  headless       opencode blocked   pid 48122 since 13:58 `glm-5.3-flash`
  last     13:39:02 question to_planner round 2
  pending  question round 2 -> mastermind

docs     /home/me/.local/state/relevo/.worktrees/docs  round 3   PAUSED
  MasterMind  architect-14    claude   route wait
  runner  headless       claude   idle      pid 51204 since 14:31 `sonnet`
  last     14:28:40 report to_planner round 3
  pending  --

api      /home/me/src/shop                        round 4   ACTIVE  +120/-30 in 6  quiet 40s  ●new
  MasterMind  architect-14    claude   route wait
  runner  headless       opencode working   pid 51777 since 14:02 `glm-5.3-flash`
  last     14:02:11 report to_planner round 4
  pending  report round 4 -> mastermind

candidates
  agy/google/gemini-3.8-flash-high  rate-limited  13:58  until 15:30

1 done · relevo unbind --done to clear

Rows are ordered NEEDS YOU, PAUSED, ACTIVE, DONE — attention first, stale first inside a group, newest last-event first — the same order relevo ui uses, so the two never disagree. DONE rows are hidden by default and the footer counts them; --all includes them, so --all is what shows the work you finished and not yet cleared. Naming a binding shows only that one.

While a round is open, the row also carries the round’s live diff against its baseline (+120/-30 in 6, or (shared tree) for a --cwd binding), an ACTIVE row’s quiet <age>, and ●new when the newest report has not been read. --json adds the fields the prose does not spell out: branch, waiting (cause, line, since, hint), last_seq, live, quiet_for and unread.

relevo status --line — under the Claude Code prompt

demonstrationauthored from relevo’s output formats · not a capture of a session

> MasterMind architect-14
○ api     r3 · builder · prompt sent                                  12m
○ plan    r1 · planner                                ARTIFACT IN    4m
● client  r1 · builder · question in                   NEEDS YOU     2m

One row per live binding of this MasterMind: ○ name rN · <actor>, plus on <candidate>[@server] when the runner is remote, what the round is waiting on, this round’s tokens, then the status column — ARTIFACT IN for a delivered reader output, REPORT IN for a writer’s, QUESTION IN, NEEDS YOU, or the round’s phase when there is none — and the clock, last, ticking while the round runs and frozen once the report is in. ● marks a NEEDS YOU row. The first line names the MasterMind, so each terminal says which one it is. It shows nothing on error and never probes a builder. One line in ~/.claude/settings.json:

"statusLine": { "type": "command", "command": "relevo status --line", "refreshInterval": 1 }

relevo ui — the cockpit

demonstrationauthored from the reader’s own golden render · not a capture of a session

 relevo   6 bindings · 1 needs you · 1 paused                                                               agy gated until 15:30  ·  14:02

 NEEDS YOU  1                     │ atlas  round 4   ACTIVE
▎  webshop                     r4 │ MasterMind  architect-14          claude    working
▎  question · 2m                  │ runner  %11          opencode  working
▎  dirty · 2 consults · $9.40 · 1 │ tree     relevo/atlas
▎  opencode · relevo/webshop      │ usage    live · glm-5.3-flash · 4m · in 2k · cache 91k (95%) · write 3k · out 8k · $0.04
                                   │ spend    1 round · $0.16 · 3.5M tok
 PAUSED  1                            │
   docs                        r3 │  prompt    report    terminal    diff    log
   released between rounds        │ ──────────────────━━━━━━━━━━────────────────────────────────────────────────────────────────────────
   claude · bind --resume         │ %11 · captured 0s ago · 2 lines
                                   │
 ACTIVE  3                        │ $ go test ./...
   api                         r2 │ ok      github.com/fuad-daoud/relevo/internal/ui    1.2s
   working · quiet 23s            │
   opencode · relevo/api          │
▸  atlas                       r4 │
   working                        │
   $0.16 · 3.5M tok · live $0.04 ·│
   opencode · relevo/atlas        │
   worker                      r2 │
   working                        │
   opencode · relevo/worker       │
                                   │
 DONE  1                            │
   ledger                      r3 │
   done · 3h                      │
   agy                            │
                                   │
↑↓ move   ⏎ open round   tab next tab   [ ] round   1-5 tab   s sort: attention   c compact   : command   q quit   webshop NEEDS YOU

The views read live and archived data. The cockpit is not read-only: every action goes through an explicit key and confirmation (x stop, D done, u unbind, g gate, s send, r retry, o shell, E editor, b bind), and the :settings, :actors, :candidates, :agents and :servers sections are editable in place. It still never types into a terminal. Its views are :fleet, :rounds, :round, :stats, :candidates, :actors, :agents, :servers, :settings, :audit and :log. :servers lists the configured servers and probes their health on demand (r); you can add (a), edit (e/enter) and delete (d) a server there; the client key stays with relevo config server key and the view never shows or edits it. Five tabs per round — prompt, report, terminal, diff, log — and [ ] step back through every round the binding has run. :rounds opens every round the database has recorded, live or archived, with relevo history’s filter grammar on top.

relevo show — one round, after the fact

relevo show <name> prints one section of one round: --prompt (the default), --report, --diff with --stat or --anchors, --drift, --log with --follow and --after N, --transcript, --gate, --findings, --output or --artifacts, --artifact <rel> (raw bytes) and --peek (read without claiming the pending payload). A live binding is read from its open round’s files; anything sealed or archived is read from the database and renders the same way. relevo history -q "harness:agy outcome:halted since:30d" is round history across every binding relevo has recorded, and --by regroups it.

The daemon

relevo daemon is the reconciler and the one process that opens relevo.db; every other process reaches the database through the owner socket $XDG_STATE_HOME/relevo/relevo.sock beside it, so a command that finds no daemon starts one itself — the systemd user unit when installed, launchctl kickstart on macOS, otherwise a relevo daemon of its own appending its output to <state>/daemon.log. So the CLI works on its own: you never have to start the daemon first, and reports are delivered even when you run a single verb by hand instead of relevo wait. Run it in any spare terminal, or hand it to systemd or launchd:

relevo daemon
  # the reconciler, in the foreground
relevo daemon --check
  # exit 0 when one is running, 1 when not
relevo daemon --pprof <path>
  # net/http/pprof on a 0600 unix socket; off by default
make service
  # a systemd user unit, or a LaunchAgent on macOS

Only one daemon runs at a time. It takes an exclusive lock on $XDG_STATE_HOME/relevo/.daemon.lock and refuses to start beside another, so a second one started by hand is an error rather than two reconcilers racing. A running daemon moves onto a newly installed binary by itself within a few seconds, and a round in flight is not interrupted.

The four states relevo status shows

ACTIVE
Someone is working. Nothing needs a human yet. A row may carry stalled, exploring or quiet — observations, never actions: relevo never kills, nudges or switches on them.
NEEDS YOU
relevo has stopped and a person must act: a dead builder process, a round past its budget, a binding at its round cap, a permission denial. relevo flags it and reports it; it never cleans it up.
PAUSED
The binding’s worktree was released between rounds, keeping the branch and the round log. Nothing needs a human, unbind --done leaves it alone, and relevo bind --resume restores it.
DONE
You declared the work verified with relevo done. Relaying stopped deliberately, not because anything went wrong: no reports are queued and no timeouts are flagged. A clean worktree is released so the branch is free to review.

Mid-round switching

The daemon can replace a builder while a round is open, in two cases: its process exits without writing a report, or you gate its provider with relevo gate — which is how you tell relevo a running builder hit its limit. It resolves builder again through the actor’s ordered candidate list and the ledger, starts the pick in the same tree, and hands it the same round’s plan. The round number does not change; the round clock restarts. A switch entry in the log says what was tried and why. Bounded by max_switches, default 2; after that the binding goes NEEDS YOU.

Hooks and webhooks

The daemon runs any executable in ~/.config/relevo/hooks/<event>.d/ — state_changed.d/, round_started.d/, builder_stalled.d/ and binding_stale.d/ — detached, with a 10-second timeout and RELEVO_EVENT, RELEVO_BINDING, RELEVO_STATE, RELEVO_OLD_STATE and RELEVO_ROUND in the environment. A directory is imported into the hooks section and removed; an event with no argv list is a silent no-op. notify.webhooks in the policy section posts the same events straight to a URL with no script required — a Slack incoming webhook, a Discord webhook, or any endpoint that accepts a JSON POST, in the slack, discord or json format, filtered by event.

Between rounds

Verbs for the space after one round closes and before the next opens. Each is the mechanical half of something the MasterMind used to type by hand, and each stops short of the decision.

The check

A binding can carry a gate command: a shell line relevo runs in the worktree the instant the completion marker appears, against the tree exactly as the runner left it. Configure it with --gate 'make check' on bind; --no-gate opts a binding out of the policy section’s gate.default, and omitting both flags falls back to it. The gate never decides anything — it annotates. The round still closes on the marker, the report is still delivered, and the human still judges the diff; the gate only adds a gate=<result> note, whose result is one of pass, fail, timeout or error.

demonstrationauthored from relevo’s output formats · not a capture of a session

Gate: make check -- FAIL (exit 2, 1m40s). Output: /home/me/.local/state/relevo/api/003-gate.log
  ok  	github.com/example/pkg	0.01s
  FAIL	github.com/example/pkg2	0.02s

The gate’s full output and the supervisor’s exit trailer live in NNN-gate.log beside the round, and relevo show <name> --gate prints it. A failing gate’s payload line carries the last few non-empty lines of that log, so the MasterMind sees why without opening the file.

The reviewer

relevo send --verify — or the policy section’s verify.default — marks the round: when it closes, after the gate, so the reviewer sees the gate’s own output, relevo runs a read-only reviewer over the finished round and records its verdict. --no-verify overrides the policy default for one send; the two flags are exclusive.

The reviewer is a headless, one-shot reader started at round close in a throwaway worktree at the runner’s HEAD, removed once it reaches any terminal state. That isolation is what lets it run tests without touching the builder’s tree or your own checkout. It ends its findings with verdict: accepted or verdict: rejected; anything else — no block, an unreadable one, a verdict that is neither word — is recorded as unstructured and delivered as prose. A verdict decides nothing: rejected does not reopen the round, stop the binding or summon a human, and the report is delivered exactly as it always was.

Repair rounds. A failing gate does nothing on its own. A binding can opt into a repair round instead, with --regate N on bind or send, or with regate under gate in the policy section. relevo then opens at most N more rounds, each naming the failed check and the last 200 non-empty lines of its log, and telling the builder to fix only what the check reports. 0, the default, turns the loop off.

Done and unbind

relevo stop <name> ends an open round on purpose. The runner has no stdin, so there is nothing to ask it: the process is killed and the round closes without a report. A stop is not a failure — it never charges a mid-round switch and never gates a candidate. send --candidate is refused while a live round is open — except on a served binding halted in NEEDS YOU, where stop is itself refused (the server answers 409 round_halted) and naming the candidate re-points that round. relevo done <name> declares the work verified and stops relaying: when the worktree is clean and no round is open, it releases the worktree so the branch is free to check out, and relevo bind --resume --name <n> puts it back on the same branch at the same path.

relevo unbind <name> forgets a binding, deleting its directory. Archiving is the default where a binding is cleared in bulk: relevo unbind --done clears every binding the MasterMind marked DONE in one pass, archiving each, and --delete removes them instead. An archived binding keeps every round, event and artifact, so relevo history and relevo show still read it months later, and the name frees for a fresh binding. --sweep clears the branches and refs of bindings that no longer exist, once each is on a remote-tracking ref. A PAUSED binding is left alone: it is released but alive.

relevo deletes a branch in exactly two places: unbind --done and unbind --sweep, for a relevo/<name> branch relevo created, and only once its commit is on a remote-tracking ref; and a bind --server rollback seconds old, when the server refuses the binding. A branch adopted with --branch is never deleted — relevo did not create it.

Candidates, actors, gates

A candidate is one way to run an actor, named by the token harness/provider/model. harness and provider are single segments and model is the rest, so opencode/openrouter/z-ai/glm-5.3-flash is one token. Some harnesses take an effort suffix on the model: claude reads opus:medium as --model opus --effort medium, codex reads gpt-5.6-terra:high as a reasoning effort, and opencode takes a variant as model#variant. The suffix stays in the token, so two efforts are two candidates.

relevo ships no candidates. Which model you are entitled to run is a fact about your accounts, not about relevo. Candidates, the actors and the policy live in relevo.db, not in a file, and are read and changed with relevo config: relevo config get candidates, relevo config set actors.builder.candidates '[…]', or relevo config edit for the whole document.

Each actor holds an ordered candidate list, most preferred first. Omit --candidate and relevo takes the actor’s first candidate that is not gated; name one and relevo starts exactly that, gated or not. When several candidates serve an actor and nothing orders them, relevo refuses rather than picking, and says so as would refuse.

demonstrationauthored from relevo’s output formats · not a capture of a session

$ relevo config
builder  (config actors)
  1  gemini-3.8-flash-high    order     rate-limited until 20:28
  2  sonnet                   order     <- would pick
  3  glm-5.3-flash            order     limited 2x around 14:00 (30d)
reviewer  (config actors)
  1  opus                     sole      <- would pick

The marker is computed by the same code bind runs, so it cannot disagree with what bind does next, and every pick is written down: one line on stderr and a pick entry in the binding’s log.

Gates

A limit gates the provider — every candidate with that provider — because that is who enforces the quota, not the model. Without --for it stays gated until you clear it, because relevo does not know your provider’s reset schedule. relevo also records spawn failures itself, gating that one candidate for ten minutes; those expire on their own. Name a gated candidate explicitly and relevo prints a note: and proceeds — you named it.

relevo gate claude/anthropic/sonnet --reason "5-hour window"
relevo gate claude/anthropic/sonnet --for 2h
relevo gate --clear anthropic

The same command tells the daemon to replace a running builder that hit its limit: see mid-round switching. relevo status gains a candidates block only while something is gated, and relevo config marks a gated row unavailable:. relevo gate --serve acts on the local serve daemon’s gates.

What relevo refuses to do

relevo makes no judgements. It moves files, starts processes, and records what each round did. Whether a report is good, whether a question needs a human, whether the work is done — every one of those decisions stays with the MasterMind, or with you. Six structural refusals follow from that, and each has a reason.

It makes no judgements

relevo never summarises, rewrites, or decides that work is done. It reads a report for the shape of its block, never for meaning, and it grades nothing. The decision is the one thing it does not automate — that is the whole product.

The check annotates; it never decides

A gate result is a note on the round. A reviewer’s verdict is a note on the round. A round budget that runs out flags NEEDS YOU and kills nothing. relevo reads no agent’s claim for meaning, and the human still judges the diff.

The destructive verbs need a name

done and unbind act on one loop and take its name, or --pick. Neither resolves the current directory for you, and a bare verb never opens a picker: a bare done once ended a live loop by accident, and the recovery is relevo bind --resume.

It refuses to guess a candidate

More than one candidate serving an actor with no order set is would refuse, not a pick. relevo lists them and names the command that sets the order, rather than choosing for you.

It deletes a branch in exactly two places

unbind --done and unbind --sweep, for a relevo/<name> branch relevo created and that is already on a remote-tracking ref; and a bind --server rollback seconds old. A branch holds commits, and commits are work; an adopted --branch is never deleted.

Nothing is typed into your session

A report reaches the MasterMind by the background wait, the channel, a harness deliverer, or a relevo wait you run by hand — and by nothing else. relevo never writes into the session you are talking to.

Also in the box

bind
Bind this MasterMind to a runner over the current working tree. --worktree attaches another on its own worktree, --server runs it on a remote host, --cwd binds an existing directory. A fresh bind names exactly one of --feature L and --no-feature, plus --ticket, --actor, --candidate, --tier, --gate, --no-gate, --regate and --local.
send
Stage a plan file as the current round and start the runner: --file, --candidate, --tier, --dry-run, --verify|--no-verify, --regate.
status
One row per binding: round, state, the runner’s live status, and what is pending. --all includes DONE rows, --line is the status-line form, --name shows one binding, --json the machine-readable one.
history
Round history as JSON across every binding, live or archived: --here, --binding, --feature, --ticket, --mastermind, --since, --limit, -q QUERY, --by, --rows, --json.
show
One round’s prompt, report, diff, drift, gate, findings, log, transcript or output file, live or archived: --round N, --diff [--stat|--anchors], --log [--follow --after N], --peek, --artifact <rel>, --json.
wait
Block until a round closes or the binding needs you, then print the pending report. --timeout, --round, --any, --peek, --json.
ui
The cockpit: :fleet, :rounds [query], :round <binding> [N].
done
Mark a binding done; relaying stops. --pick chooses it on screen.
stop
Kill the runner process and close its round without a report, unless one is already on disk.
unbind
Forget a binding, deleting or archiving its directory. --done clears every binding the MasterMind marked DONE, with --delete or --dry-run; --sweep clears the branches of bindings that no longer exist.
daemon
Run the long-running reconciler: --interval, --check, --pprof <path>.
mcp
Run an MCP server over stdio for a Claude Code MasterMind: status, send, show, gate and done as tools; in channel mode it also pushes reports and NEEDS YOU into the session.
doctor
Preflight check: plugin, daemon, harness binaries, roles, the database and hooks. It reports; your shell acts.
update
Replace this release binary with the latest release, checksum-verified: --check, --to vX.Y.Z, --release.
bugreport
Assemble a local, redacted bug-report bundle — version, machine, doctor’s checks, bindings, the last recorded failure — and print the gh issue create line; nothing is sent automatically. --name/--round, --logs, --raw, --out, --stdout, --json, --gh.
config
Show the actors, the current pick and the candidates, and read or change the configuration document: init, agents, edit, get, set, unset, export, import, server add|rm|list|key, secret set|rm|list.
mastermind
Register this MasterMind, or re-attach an existing one, and list, rename or forget records.
gate
List this machine’s active gates; gate a provider with relevo gate <token> [--for D] [--reason S]; clear one with relevo gate --clear <provider|token>. relevo gate --serve acts on the local serve daemon’s gates.
serve
Run the remote-builder server, listener and daemon. Server administration, on the server host: init, enroll, clients, revoke, fingerprint, status, ui, gc, unbind.
help
Print the command list; --json prints the registry document (verbs, flags, output documents, error codes). A failure is coded — relevo: <code>: <message> then a next: line.
version
Print the relevo version.

Renamed commands. Every verb the housekeeping removed, and the form that replaces it. An old name exits 2 and names its replacement: init → relevo config init · candidates, policy, roles → relevo config · agent → relevo config agents · client → relevo config server · servers → relevo config server list · add → relevo bind --worktree · diff → relevo show --diff · log → relevo show --log · gc → relevo unbind --done · pause → relevo done, then relevo bind --resume · statusline → relevo status --line · pull → relevo wait, which prints the report · unavailable → relevo gate <token> · available → relevo gate --clear <provider> · db → relevo doctor, the database row.

Install

In Claude Code, as a plugin:

/plugin marketplace add fuad-daoud/relevo
/plugin install relevo@relevo

Then, once, on a clean machine:

relevo config init
  # candidates, policy and actors
relevo doctor
  # names what is missing and prints the fix

Or take the binary: a release tarball for Linux or macOS, amd64 or arm64, unpacked onto your PATH — or, with a Go toolchain,

go install github.com/fuad-daoud/relevo/cmd/relevo@latest

A release binary updates itself with relevo update; a go install gets its own command printed instead.

Requirements

Two harnesses
One for the MasterMind, one for the builder. relevo knows how to start claude, opencode, agy and codex; claude only plans, so it never gets a builder candidate. There is no terminal manager and no terminal multiplexer to install.
git
Optional. Without it relevo works normally, but rounds capture no diffs.
Linux or macOS
Both are exercised in CI. Windows is not supported: the tree cross-compiles for windows/amd64 and refuses at runtime with a clear error rather than running without a state lock.
Go 1.25+
To build from source. Not needed for the plugin or a release binary.

Upgrading

relevo update installs the latest release over the running binary by an atomic rename, and --check prints what it would do without changing anything; --to vX.Y.Z installs an exact tag, a downgrade included. A running daemon moves onto the new binary by itself, and a round in flight is not interrupted.

Claude Code caches an installed plugin by version, so update it to match:

claude plugin marketplace update relevo && claude plugin update relevo@relevo

The remote ends upgrade together: a pre-upgrade client against an upgraded server gets HTTP 426, and an upgraded client against a pre-upgrade server is told the server predates audience-bound signatures.