This guide is for coding agents and the people who run them: when Dispatch fits, how to install it, how to read its verdicts, and which actions need the user's authorization. The same page is available as Markdown at https://rundispatch.sh/agents.md. It describes Dispatch 0.4.11.
When to use Dispatch
Use it when:
- The repository changes while an agent works: you commit, you pull a teammate’s merge, or another agent’s Work lands.
- Several agents work in separate worktrees on one repository and land one at a time. After each landing, Dispatch judges the rest against the new code.
- You are about to integrate a change an agent made from an older snapshot, and you want to know whether it still holds, with reasons, before it lands.
It adds little when:
- One agent works on a repository nothing else changes during the task. The code did not move, so the verdict is CONTINUE.
- You need a security sandbox. Agents and checks run with your operating-system permissions.
- You want stale Work fixed for you. Dispatch does not rebase, resolve conflicts or rerun an agent by itself; it tells you to redo the Work.
- You need a hosted service or a server-side merge queue. Dispatch is a local CLI with no server.
- You need symbol-level analysis outside Rust and Python. Other languages get file-level facts: any change to a file the Work edits or names is a REFRESH.
- You are not on macOS arm64 or Linux x86_64. Those are the only prebuilt releases; other platforms are untested.
A rebase and your tests tell you whether the merge is clean and the tests pass. Dispatch also keeps the snapshot the Work started from, names the declaration it relied on that changed, and blocks dispatch accept until the Work is redone.
Dispatch judges the local checkout it watches. A teammate’s merge counts once you pull it into that checkout. The verdict gates dispatch accept, which applies Work only when it still holds. Interactions between pieces of Work are advisory and block nothing.
Before you start
- Prebuilt releases: macOS arm64 and Linux x86_64. You need Git, and the tools the project's own checks use. There is no account, and nothing is uploaded.
- Agents need no protocol to be checked. An agent working inside a workspace Dispatch made or observes should just do its task. Checking and integrating are for whoever runs Dispatch.
- See whether Dispatch is already installed:
command -v dispatchanddispatch version. This guide describes 0.4.11. For any other version, read the README at that version's tag:https://github.com/rundispatch/dispatch/tree/v<version>#readme.
Authorization
Some Dispatch actions record a decision or acknowledge a risk on the user's behalf. Take them only with the user's authorization. Authorization can come from:
- the current request (for example, "integrate it if it still holds");
- an earlier explicit instruction in the session;
- a standing rule the user set, such as an instruction in their AGENTS.md or CLAUDE.md.
When authorization exists, act on it without asking again. When it does not, report the verdict and the exact command the user can run.
| Action | What it does | Needs |
|---|---|---|
dispatch check, status, diff, history, explain, piped watch |
Read state; change nothing | Nothing |
| Installing Dispatch | Downloads and installs a binary | The user's request or standing permission to install tools |
dispatch start / dispatch stop |
Start or stop one background watcher for the project | The user asked you to watch the project; stop only watching you started |
dispatch attach -- <agent> |
Runs that agent, with the user's operating-system permissions | The user asked you to run that agent |
dispatch accept <run-id> |
Applies the Work if it still holds and records the user's acceptance | Authorization to integrate |
dispatch reject <run-id> |
Records a rejection; the source is untouched | Authorization to reject |
--auto-apply |
Applies eligible Work without a human review | The user turned it on |
--allow-unsafe-local |
Acknowledges that agents and checks run with the user's permissions; not a sandbox | The user's acknowledgment |
dispatch accept --despite-refresh --explanation "<why>" |
Overrides a REFRESH that comes only from the analysis | The user's decision and reason |
dispatch setup, dispatch setup --checks |
Sets up agent profiles, hooks and checks that may run by themselves | The user, interactively |
dispatch clean --yes |
Removes workspaces of finished Work without asking | Authorization to delete them |
Install
Download the archive for the platform and SHA256SUMS from the release,
verify the checksum, then install. Stop if verification fails.
macOS arm64:
curl -LO https://github.com/rundispatch/dispatch/releases/download/v0.4.11/dispatch-macos-arm64.tar.gz
curl -LO https://github.com/rundispatch/dispatch/releases/download/v0.4.11/SHA256SUMS
grep 'dispatch-macos-arm64.tar.gz' SHA256SUMS | shasum -a 256 -c -
tar -xzf dispatch-macos-arm64.tar.gz
mkdir -p "$HOME/.local/bin"
install -m 0755 dispatch "$HOME/.local/bin/dispatch"
"$HOME/.local/bin/dispatch" version
Linux x86_64:
curl -LO https://github.com/rundispatch/dispatch/releases/download/v0.4.11/dispatch-linux-x86_64.tar.gz
curl -LO https://github.com/rundispatch/dispatch/releases/download/v0.4.11/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing
tar -xzf dispatch-linux-x86_64.tar.gz
mkdir -p "$HOME/.local/bin"
install -m 0755 dispatch "$HOME/.local/bin/dispatch"
"$HOME/.local/bin/dispatch" version
dispatch version must print dispatch 0.4.11. The macOS binary is
unsigned and not notarized. Other platforms can build from source with
cargo install --git https://github.com/rundispatch/dispatch --tag v0.4.11 --locked;
they are untested.
The shortest workflow
cd my-project
dispatch start # watch this project in the background
dispatch attach --task "<task>" -- <agent command> # prints the run ID when the agent exits
dispatch check <run-id> --json # preliminary verdict; read validity.decision
dispatch accept <run-id> # only with authorization; or: dispatch reject <run-id>
dispatch stop # when the user is done watching
Always name the run ID. Without one, commands resolve the latest run for the
current source, which is ambiguous when several pieces of Work exist.
dispatch history lists recent runs with their IDs, including Claude Code
sessions that registered themselves. Piped or with --json, dispatch watch
keeps streaming until it is stopped, so do not wait for it to exit.
Reading a verdict
dispatch check exits 0 for CONTINUE, REFRESH and STOP alike (and 1 only
when it cannot evaluate). The exit status is not the verdict: read
validity.decision.
check is preliminary. It asks whether the patch still applies and whether
what the Work relied on still holds. The project's checks run on the merged
code only at dispatch accept. A CONTINUE from check is not permission to
integrate.
validity.decision |
Meaning | Next step |
|---|---|---|
continue |
Nothing the Work relied on changed, as far as the analysis sees | With authorization, dispatch accept <run-id>. Accept can still refuse if the checks fail on the merged code |
refresh |
The patch no longer applies, something it relied on changed, or a check failed on the merged code | Nothing is applied. Redo the Work on current code: dispatch refresh <run-id> for a run Dispatch launched, or run the agent again and attach it. Or reject, with authorization |
stop |
The same change is already in the source (landed_by names the run) |
Nothing is applied. Reject it, with authorization |
The integration gate
dispatch accept <run-id> is the only way Work should land. It evaluates the
Work again against the code as it is now, runs the project's checks on the
merged tree when the code moved, and applies only if the Work still holds.
It applies the patch to the watched checkout's working tree and does not
commit: committing and pushing stay with the user's usual workflow. When it
refuses, it exits 1, prints the reason, and leaves the source and the review
untouched:
$ dispatch accept <run-id>
error: source has changed; this work is stale (REFRESH): pub fn validate(token: &Token) -> Result<User, AuthError> => pub fn validate(ctx: &AuthContext, token: &Token) -> Result<User, AuthError>. The source was left unchanged. Run your agent again on the current source and attach it, or run 'dispatch reject 01M4MC7975JQMBESM9QN7EVP3A'.
# exit status 1
Do not route around it. Do not git apply the output of dispatch diff, and
do not merge or cherry-pick the dispatch/<run-id> branch or copy files out
of the workspace. Building or testing a copy in a scratch directory to
investigate is fine; applying it to the project is not.
--despite-refresh --explanation "<why>" applies a REFRESH that comes only
from the file and symbol analysis, after the project's checks pass on the
merged tree. It never overrides STOP, a patch that no longer applies, or a
failed check. Use it only on the user's decision.
Auto-apply
Auto-apply is a supported way for Work to land without anyone running
accept. It is switched on explicitly: --auto-apply on dispatch run,
dispatch refresh or dispatch attach, or Shift+Tab in the interactive
session. It applies Work only when the evidence is complete for the exact
code it lands on. It records applied_by: auto_apply and leaves the review
pending, so it is never a human acceptance. Do not turn it on to avoid asking
for authorization.
JSON output
dispatch check <run-id> --json prints one object. These were captured from
the 0.4.11 release binary (pretty-printed here):
CONTINUE:
{
"run_id": "01M4MC76XZBQNN28AWZAAHFC3A",
"validity": {
"analysis": "files_only",
"changed_files": 2,
"decision": "continue",
"evaluated_at": "2026-10-11T02:21:12.278290Z",
"reasons": [],
"world_changed": true,
"world_digest": "56a12d4ebe870bc1078205ea7aaac5844b86dd5b0da9735109002a47cff1df45"
}
}
REFRESH, with the declaration that changed:
{
"run_id": "01M4MC7975JQMBESM9QN7EVP3A",
"validity": {
"analysis": "symbols",
"changed_files": 1,
"decision": "refresh",
"evaluated_at": "2026-10-11T02:21:14.401737Z",
"reasons": [
{
"code": "fact_broken",
"detail": "pub fn validate(token: &Token) -> Result<User, AuthError> => pub fn validate(ctx: &AuthContext, token: &Token) -> Result<User, AuthError>",
"fact_id": "6cc9cad0a4ec78b4",
"path": "src/auth.rs"
}
],
"world_changed": true,
"world_digest": "f4d56c0467c1cf486407268db74cb230a11a11dd1085c60f91e46a60cc4ae547"
}
}
STOP, with the run that already landed the change:
{
"landed_by": "01M4MC7BA1614YQ7S110PNEN4Y",
"run_id": "01M4MC7CP80ZBSRS8TPY1AEPGR",
"validity": {
"analysis": "files_only",
"changed_files": 1,
"decision": "stop",
"evaluated_at": "2026-10-11T02:21:17.757369Z",
"reasons": [
{
"code": "already_applied",
"detail": "the patch is already present in the source",
"fact_id": null,
"path": null
}
],
"world_changed": true,
"world_digest": "7dec016614a09eb3e3b6f2891c8dac65cf48613d93da45f7621f560d2df35678"
}
}
validity.decision:continue,refreshorstop.validity.reasons[]:code(for examplefact_broken,already_applied),detail,pathandfact_id.validity.analysis: how deep the analysis went, for examplesymbols(Rust and Python declarations) orfiles_only. Core also documentsintegration, for a verdict the merged-tree checks decided.check --jsonhas noschema_versionfield.
dispatch status <run-id> --json and dispatch run --json carry
schema_version: 1. After an attempt to integrate, read outcome:
outcome.application: for exampleapplied,not_appliedorblocked_by_source_drift;outcome.review:pending,acceptedorrejected;outcome.applied_by:humanorauto_apply.
dispatch run exits 0 when a result is ready (verification passed or not
configured), 3 when configured verification failed, 4 when it is waiting on a
human, 124 at the deadline, 1 on failure and 2 on a usage error.
dispatch accept exits 0 when it applied the Work and 1 when it refused or
failed.
Work against Work
While a project is watched, Dispatch compares the Work that is not yet
integrated, and says where two pieces touch: the same declaration or file,
overlapping lines, a signature one changes and the other uses, or a file one
changes and the other relies on. dispatch status <run-id> explains it
under Concurrent. watch --json work objects, and status --json while the
project is watched, carry interactions. It is
advisory: Work can be CONTINUE and still interact, and nothing is blocked or
reordered. Once one piece lands, the others are judged against the new code.
Claude Code, Codex and other agents
- Claude Code. With the hooks from
dispatch setup(Runtime integrations), a Claude Code session in its own worktree of a watched project (claude --worktree) becomes Work by itself. Each session ends tracked, pending, or told it is not tracked. A session working directly in the checkout is not tracked. - Codex, Cursor, scripts. Run them through
dispatch attach -- <agent command>. Dispatch makes an isolated workspace and judges the Work when the agent exits. Codex has no hook integration. - Dispatch launches the agent.
dispatch run "<task>"needs a profile fromdispatch setup, or--agent claude|codex|cursor, plus--allow-unsafe-localin scripts.
Boundaries
- Local CLI: no account, no server, no upload. Agents use their own logins.
- Not a sandbox: agents and checks run with the user's operating-system permissions.
- Symbol-level facts for Rust and Python; file-level facts elsewhere. A change the analysis cannot see is caught only if the project's checks cover it.
- Experimental developer preview. On Dispatch's own 36-scenario fixture matrix there are zero false CONTINUE and zero false REFRESH verdicts. Rates on real repositories are not measured, and no time, token or cost saving is claimed.