Skip to content
Experimental developer preview · 0.4.11

Keep autonomous software work validwhile the code moves.

Dispatch is the local CLI for validating coding-agent changes. Your agents start from a snapshot; while they work, you commit, teammates merge and other agents land. Dispatch checks each agent’s change against the code as it is now, before it lands, and tells you whether to continue, refresh or stop.

rundispatch.sh · github.com/rundispatch/dispatch · binary dispatch · macOS arm64 · Linux x86_64

How Dispatch worksTwo agents, two versions of the code
How Dispatch worksAgent A and Agent B start from the same snapshot of main. Agent B works in parallel and lands first, so main now contains changes Agent A never saw. When Agent A finishes, Dispatch checks its change against main as it is now and returns CONTINUE, REFRESH or STOP.main · both start herechanges Agent Anever sawAgent Blands firstB landsAgent Aon the old snapshotits changemain nowchecks againstDispatchDoes this change still fitthe code as it is now?
  • CONTINUE

    Still fits. Your checks run on the merged code first.

  • REFRESH

    Something it relied on changed. Nothing applies; redo it on current code.

  • STOP

    That change already landed. Nothing is applied.

The problem

The agent finished the task. The Work is already stale.

An agent can do exactly what you asked and still hand back a change that no longer fits the code:

  • Stale: a function it calls changed signature.
  • Conflicting: someone else edited the same function.
  • Redundant: the same change already landed.

A clean git apply doesn’t mean the change still fits, and every extra agent is one more thing moving the code. The goal isn’t more agent output; it’s more of it landing correctly.

What the agent saw · S0

// src/auth.rs

pub fn validate(token: &Token) -> Result<User, AuthError>

// src/handler.rs, the agent’s patch

+ let user = auth::validate(&token)?;

The code now

// src/auth.rs, changed by a teammate

pub fn validate(ctx: &AuthContext, token: &Token) -> Result<User, AuthError>

The patch still applies. The call no longer compiles.

When to use it

When Dispatch helps, and when it doesn’t.

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 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.

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.

The full list, and how agents should operate Dispatch, is in the agent guide.

How it decides

A clean git apply is only the first question.

Every piece of Work carries the code it started from, S0, and the change it produced, Δ. Dispatch asks three questions of Δ against the code as it is now. Each one catches what the one before it can’t see.

The verdict is recomputed from S0, Δ and the code now every time, never trusted from an earlier moment. dispatch check asks the first two, so its CONTINUE is preliminary; accept asks all three.

Δ

The agent’s change, kept with the snapshot it started from (S0).

  1. What a clean git apply tells you

    Does the patch still apply?

    Δ against the code as it is now.

    Catches: Someone edited the same lines, or the same change already landed.

    REFRESHno longer applies

    STOPalready there

  2. What Dispatch adds

    Does what it relied on still hold?

    The declarations Δ edits and the ones its new code calls, compared between S0 and now. By symbol for Rust and Python, by file elsewhere.

    Catches: A function the patch calls changed its signature. The patch applies; the call is broken.

    REFRESHa fact broke

  3. What Dispatch adds

    Do your checks pass on the merged code?

    At accept, when the code moved: your checks.verify on the merged tree, in a scratch copy. Your source is untouched until they pass.

    Catches: Behavior changed somewhere the analysis cannot see, and a test now fails.

    REFRESHa check failed

  4. CONTINUE

    All three hold. The change is ready for your accept, or for auto-apply if you turned it on. A REFRESH or STOP applies nothing and leaves the review pending.

Examples

What that looks like.

01Unrelated changeThe code moved, but not under the Work.

The code moved, but not under the Work.

Attached Work adds a call to auth::validate(&token) in src/handler.rs. While it waits for review, you edit README.md and an unrelated function in src/util.rs.

Nothing the Work relied on changed. This is a preliminary verdict: at accept, Dispatch evaluates again and runs your checks on the merged tree first.

$ dispatch checkRun <id>Coherence: CONTINUEAnalysis: files_onlyWorld changed files: 2Next: dispatch accept <id>
02Signature changedA function the agent calls changed underneath it.

A function the agent calls changed underneath it.

Same Work. Instead, someone changes the signature of validate in src/auth.rs. The patch still applies cleanly, but it calls the old signature.

Attached Work is redone by running your agent again on current code and attaching it. For a run Dispatch launched, the next step is dispatch refresh, which starts a new run of the same task.

$ dispatch checkRun <id>Coherence: REFRESHAnalysis: symbolsWorld changed files: 1  fact_broken: pub fn validate(token: &Token) -> Result<User, AuthError> => pub fn validate(ctx: &AuthContext, token: &Token) -> Result<User, AuthError>Next: run your agent again on the current source and attach it (or dispatch reject <id>)
03Other Work landedAnother piece of Work landed first.

Another piece of Work landed first.

Two runs started from the same snapshot and made the same change. Run A is accepted. Run B is judged against the code Run A produced.

Accept applies first and records your acceptance after. Work integrates one piece at a time; if Run A had changed something Run B relied on instead, the verdict would be REFRESH.

$ dispatch accept <run-a>Applied Candidate A to ~/my-project (1 file(s) changed).Review recorded (revision 1).$ dispatch check <run-b>Run <run-b>Coherence: STOPAnalysis: files_onlyWorld changed files: 1  already_applied: the patch is already present in the source  already landed by run <run-a>Next: dispatch reject <run-b>
04Checks on the mergeThe facts hold. The merged code still fails.

The facts hold. The merged code still fails.

A teammate changed a retry limit, and nothing the Work relied on moved, so check says CONTINUE. At accept, Dispatch first runs your checks.verify on the merged tree.

Nothing is applied and no acceptance is recorded: the result stays pending review. --despite-refresh cannot override a failed check.

$ dispatch checkRun <id>Coherence: CONTINUEAnalysis: files_onlyWorld changed files: 1Next: dispatch accept <id>$ dispatch accepterror: source has changed; this work is stale (REFRESH): check `python3 -m unittest -q` failed (exit 1): FAIL: test_cap (test_rates.T) / AssertionError: 60 != 30; logs: …/verify-01.stderr.log. The source was left unchanged. Run 'dispatch refresh <id>' to redo the work on the current source, or 'dispatch reject <id>'.

Output from the 0.4.11 CLI · run IDs and paths shortened

Work against Work

See two changes collide before either one lands.

Coherence checks each piece of Work against main. While a project is watched, Dispatch also compares the Work still in flight with each other, and names where two pieces already touch.

In flight · nothing has landed yet

agent-a

Scope validate to a tenant

changes validate(token) → validate(ctx, token)

app/auth.py

agent-b

Authenticate in handle

calls validate(token)

app/handler.py

agent-c

Document auth

adds an Auth section

README.md

agent-a changes the signature of validate, which agent-b uses. All three are still CONTINUE against main.

When agent-a lands, agent-b turns REFRESH and agent-c stays CONTINUE. The interaction told you which pair to look at before either one landed.

What counts as touching

  • Both change the same declaration or file, or edit overlapping lines.
  • One changes the signature of, or removes, a declaration the other uses.
  • One changes or deletes a file the other relies on.

Exact to the declaration for Rust and Python, to the file elsewhere. It’s advisory: a piece of Work can be CONTINUE and still interact, and nothing is blocked, reordered or refreshed. When one piece lands, the rest are judged against the new code as usual. Since 0.4.9, Dispatch also records whether each interaction was followed by an invalid verdict, so you can measure how often it’s right from your own state directory.

dispatch watch · status · accept · check

$ dispatch watch | cat01M4MC7G · isolated agent-a · S0 workspace at start ab027f42 · unmoved · ready · no checks · review pending · interacts with 01M4MC7H01M4MC7H · isolated agent-b · S0 workspace at start 257979f0 · unmoved · ready · no checks · review pending · interacts with 01M4MC7G01M4MC7J · isolated agent-c · S0 workspace at start cad06b0a · unmoved · ready · no checks · review pending$ dispatch status 01M4MC7H…Coherence: CONTINUE — world unchanged…Concurrent  with 01M4MC7G: it changes the signature of validate (app/auth.py), which this Work uses$ dispatch accept 01M4MC7GApplied Candidate A to ~/my-project (1 file(s) changed).Review recorded (revision 1).$ dispatch check 01M4MC7HRun 01M4MC7H…Coherence: REFRESHAnalysis: symbolsWorld changed files: 1  fact_broken: def validate(token): => def validate(ctx, token):
Captured from the 0.4.11 CLI with scripts standing in for the agents. Paths shortened; parts of the status output and the watch header trimmed. Piped, watch prints rows; on a terminal it is a table that names the other Work under TOUCHES.

How Work reaches Dispatch

Watch the project. Run your agents the way you already do.

dispatch start

Watches this project in the background and gives your shell back.

dispatch watch

A live table of Work, named by worktree folder or task, with Work that needs you first. Finish, accept, reject or review without copying an ID.

dispatch stop

Ends watching. Leaving watch doesn’t.

Attach any agent

dispatch attach -- claude

Run any agent CLI from your checkout. Dispatch gives it an isolated workspace and judges the result when it exits.

Claude Code sessions

claude --worktree

With the hooks from dispatch setup, a session in its own worktree becomes Work by itself. Each one is tracked, pending, or told it isn’t tracked; none is dropped silently. Codex uses attach.

Let Dispatch run it

dispatch run "Fix the retry race"

Dispatch launches Claude Code or Codex from a frozen snapshot. Needs a profile from dispatch setup, or --agent.

start · attach · watch · stop

$ dispatch start✓ Watching ~/my-project  Dispatch is running in the background. See it: dispatch watch · Stop: dispatch stop$ dispatch attach --task "Greet by name" -- ./agent.shWorking in ~/.dispatch/workspaces/<id> on branch dispatch/<id>; your checkout is not touched.Attached work <id> finished; next: dispatch check <id>$ dispatch watch | cat~/my-project · watched in the background since 20:21 (pid <pid>)01M4MC86 · isolated external · S0 workspace at start d180c3aa · unmoved · ready · no checks · review pending$ dispatch stop✓ Dispatch stopped watching ~/my-project.
Captured from the 0.4.11 CLI; agent.sh stands in for your agent, and IDs and paths are shortened. Piped, watch prints its rows; on a terminal it is the interactive view.

Checks, review and integration stay separate. Passing tests are evidence; your accept is the decision. Discovered Work is never applied by itself, and auto-apply is a policy you turn on.

All Work shares the same verdicts and lands one piece at a time. Neither watching nor attach needs an agent profile, and Dispatch never scans for agent processes. Details are in the docs.

Built with Dispatch

Dispatch is built with Dispatch.

Releases 0.4.9 and 0.4.10 were each built by parallel coding agents, supervised by the release before. The setup is documented, command by command.

Build with parallel agents

4

agents in parallel

Claude Code sessions, each in its own workspace, built 0.4.9 and again 0.4.10. An integrator reviewed their Work and landed it one contribution at a time.

Every landing

re-judged the rest

As each contribution landed, the others were checked against the new code. All stayed CONTINUE, and merged-tree checks ran on the accepts that followed.

2 bugs

found in its own supervision

Building 0.4.9 left two sessions untracked under heavy load. 0.4.10 fixes both causes: now every session ends tracked, pending, or told it isn’t tracked.

Two releases are a small sample. This is how we use it, not a benchmark: no time, token or cost saving is claimed. Each release’s plan in Core records what happened.

Boundaries

What’s true in 0.4.11.

  • Local. No account, no server, no upload. Agents use their own logins.
  • Blocked verdicts apply nothing. A REFRESH or STOP leaves your source untouched and the review pending.
  • Symbol-level for Rust and Python. File-level for everything else. What the analysis can’t see is caught only if your checks cover it.
  • Interactions are advisory. They change no verdict and block or reorder nothing.
  • Not a sandbox. Agents and checks run with your operating-system permissions.
  • Preview. Zero false CONTINUE and zero false REFRESH on our 36-scenario fixture matrix. Rates on real repositories aren’t measured yet.

Try it

Try it where more than one thing changes the code.

You and an agent, or several agents, on the same repo. Start with a repository you can restore.

First run
cd my-project
dispatch start                # watch this project in the background
dispatch attach -- claude     # or codex, or any agent CLI
dispatch watch                # f finish · a accept · r reject · d review
dispatch stop                 # stop watching this project