Skip to content

Agent guide · 0.4.11

Operate Dispatch from a coding agent.

Dispatch at rundispatch.sh, the local CLI for validating coding-agent changes. When it fits, how to install it, how to read a verdict, and what needs the user’s authorization.

Also available as Markdown.

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 dispatch and dispatch 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, refresh or stop.
  • validity.reasons[]: code (for example fact_broken, already_applied), detail, path and fact_id.
  • validity.analysis: how deep the analysis went, for example symbols (Rust and Python declarations) or files_only. Core also documents integration, for a verdict the merged-tree checks decided.
  • check --json has no schema_version field.

dispatch status <run-id> --json and dispatch run --json carry schema_version: 1. After an attempt to integrate, read outcome:

  • outcome.application: for example applied, not_applied or blocked_by_source_drift;
  • outcome.review: pending, accepted or rejected;
  • outcome.applied_by: human or auto_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 from dispatch setup, or --agent claude|codex|cursor, plus --allow-unsafe-local in 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.

References