Skip to content

Releases: rundispatch/dispatch

Dispatch v0.4.13

Choose a tag to compare

@github-actions github-actions released this 14 Oct 02:29
640d3db

Dispatch 0.4.13 — Work that appears and ends by itself

Run dispatch start in a project and use Claude Code the way you already do. Before 0.4.13, a
Claude Code worktree session became Work only if Claude Code's hooks were installed, and a
claude -p session never said it had ended. Now, with no hooks installed, the session's worktree
becomes Work. The Work says while its agent runs, and says when the agent is gone. Dispatch
records only what it can observe, and it still never finishes Work because a session ended.

  • Discovery without hooks. On each tick the watching owner lists the project's Git worktrees
    (git worktree list). It never walks a directory. A worktree Claude Code made (under
    .claude/worktrees/, with Claude's CLAUDE_BASE file) that appears after watching began and
    is locked by a claude session … becomes Work:

    • S0 is the worktree's merge base with the project, with full confidence: everything in a
      worktree that appeared while you watched is its session's. watch shows
      S0 merge-base c45fd977 (full; found by Dispatch), and the origin is discovered.
    • This works for claude --worktree and claude -p --worktree. A claude -p run that
      starts and ends between two ticks still becomes Work, with its session shown as ended.
    • With hooks installed too, whichever sees the worktree first creates the Work, and the other
      joins it. There is one Work per worktree.
    • If a hook could not register a session in time and told it so, Dispatch may still track its
      worktree when it finds it later.
  • Found, not tracked. Claude worktrees that were already there when watching began are not
    turned into Work, because Dispatch cannot know what they held before. Neither are new ones
    without a claude session lock, such as subagent or background worktrees, whose lock has not
    been observed yet. They are listed with the command that attaches them:

    01M4VJ2H · discovered claude · S0 merge-base c45fd977 (full; found by Dispatch) · unmoved · working · checks not run
    found, not tracked · dispatch attach --workspace ~/src/project/.claude/worktrees/old
    

    watch lists them under the Work, status in a "Found, not tracked" section, and
    watch --json in the inventory field of the watcher record. The list belongs to the
    watch: it survives an owner restarted after a crash, and dispatch stop clears it. Worktrees
    you made yourself (git worktree add) are never listed.

  • Liveness from Claude's worktree lock. Claude Code locks a worktree it made with
    claude session <name> (pid <pid> start <time>). While that exact process runs, the Work
    shows agent running (pid N). A pid reused by another process does not count. Dispatch
    records the end of the session once the process is gone and its lock is left, as claude -p
    leaves it (process_gone), or once the lock is released (lock_released). For either, it
    must first have seen the process running. The Work then reads idle, with 0.4.12's
    "session ended — finish, resume or close". Nothing is finished, applied, closed or signalled.
    An unreadable process start, or a lock never seen running, proves nothing, and nothing is
    recorded. A hook's end and a lock's end give one end. Only worktrees the runtime made are
    read; a person's attach, with or without --pid, is never touched.

  • Live progress, with no fake zeros. Working attached Work shows the files it has changed so
    far, as the owner last counted them, and how long Dispatch has tracked it:

    Candidate A (claude)
      Status          running
      Verification    not configured
      Runtime         unknown
      Tracked         for 4s
      Cost            unknown
      Task tokens     unknown
      Token basis     unknown
      Files changed   2 now
    

    Until the owner has counted, and whenever no owner watches the project, files read unknown.
    Before, status printed Runtime 0.0s, Files changed 0 and Diff +0 / -0 for every
    working attached Work. In watch the selected Work's details have a Progress line, such as
    files: 2 changed · tracked for 12m · agent running (pid 4242), or files: none changed yet
    for a count of zero. A finished result keeps the numbers recorded when it was delivered.

  • First use without a terminal ceremony.

    • dispatch start in a project without checks, on a terminal, shows the checks Dispatch
      detected, with "Continue without checks" focused, so Enter alone never writes to the
      project:

      This project has no checks. Dispatch found:
        sh ./verify.sh
        make test
      Saving them writes checks.verify in dispatch.yml; it does not let Work run them by themselves.
      

      "Save these checks" writes them to dispatch.yml. Esc starts nothing.

    • Without a terminal (a script, CI, an agent's shell), start watches at once, detects and
      runs nothing, and says:
      No checks are configured, so Work here is not verified. Add them: dispatch setup --checks, or dispatch setup --checks --verify '<command>'.

    • dispatch setup --checks --verify 'cargo test' --verify 'make lint' saves exactly those
      commands, in that order, and keeps the rest of dispatch.yml. On a terminal, --verify
      pre-fills the same menu.

    • Consent is never granted without a terminal. Letting Work run checks by itself still needs
      dispatch setup --checks in a terminal, or --allow-unsafe-local on a single finish.
      Saving other commands ends consent given for the old ones. The consent screen now says that
      checks run project code, including code an agent wrote, on this host with your permissions,
      not in a sandbox.

  • Watching no longer takes Git's index.lock. The owner runs git status in your
    repository on every tick. Git refreshes the index under index.lock when it can, so your own
    git commit could fail while Dispatch watched. That has been true since 0.4.5. Dispatch's Git
    commands now set GIT_OPTIONAL_LOCKS=0. The race showed up while building this release, as a
    test's own git commit failing under load.

Not in 0.4.13: other agents' worktree layouts are not recognized; for them,
dispatch attach -- <agent> gives the agent a workspace of its own. There is no process
scanning, and nothing is finished, closed or cleaned up automatically. A lock is read only in a
watched project.

No migration: the schema stays at 24. JSON changes are additive: the discovered provenance,
via: "discovery", the runtime.agent_running and runtime.agent_gone events, the
process_gone and lock_released end reasons, and watch --json's inventory. Once 0.4.13
has found Work, 0.4.12 cannot read that state directory. Roll back only from a backup.
"Upgrading to 0.4.13" in docs/release-install.md has the details; restart the owner after
upgrading.

Built with Dispatch: four code packets ran at the same time, each an interactive Claude Code
session that a pinned Dispatch 0.4.12 started in its own workspace
(dispatch attach --root <clone> -- claude), then a docs packet. A human reviewed and accepted
them one at a time. Checking each packet against what had already landed, by an independent
suite and by the merged checks at accept, caught three integration problems before release.
A real-agent trial with no hooks caught a fourth: a claude -p worktree session that
discovery found while it ran, and that ended before Dispatch looked again, kept its session
open; discovery now records the running session's process as the Work's agent, so its end is
seen however soon it comes.

Dispatch v0.4.12

Choose a tag to compare

@github-actions github-actions released this 13 Oct 16:24
8fc2fdc

Dispatch 0.4.12 — Review the Work you mean

While investigating field reports (a finish that said "Ready for review" while check said
REFRESH, Work that seemed to need full IDs, landed work recorded as rejected, clean leaving
worktrees behind), review found a worse problem. Two pieces of attached Work, A and B, finish one
after the other. A's finish summary ends with dispatch accept / dispatch reject, with no ID.
Following it accepts B: an ID-less accept takes the newest result waiting for that source,
applies it and records a human acceptance for it, while A stays pending. A fixture reproduced it
on 0.4.11. 0.4.12 fixes it, and the problems those reports raised.

  • Decisions name their Work, and ambiguity is refused. Every finish and result summary
    prints diff, accept, reject or close with the Work's full ID. dispatch accept or
    dispatch reject without an ID acts only when exactly one result waits for review for the
    source. With two or more, it changes nothing and lists them:

    $ dispatch accept
    error: 2 results wait for review for ~/src/project; nothing was decided. Name the one you mean:
      dispatch accept 01M4SNHQS8ZP2FEGVG5A92BNNC  (work B)
      dispatch accept 01M4SNHPQV8CY5GWW19BZ3RDSJ  (work A)
    

    Work still in progress is listed as not counted. Unique ID prefixes still resolve, and what is
    recorded names the full ID.

  • dispatch close retires Work without judging it. dispatch close <id> --reason <reason>
    records one work.closed event and no review. It never applies, commits, deletes a workspace
    or signals an agent, and it refuses Work a live attach -- wrapper still owns. The reasons:

    • unfinished: Work nobody will finish. Its changes are frozen into its patch first, without
      running checks.
    • workspace-removed: unfinished Work whose workspace is gone. Its kept changes stay.
    • already-present: a result whose whole patch is already in the source. Part of it is not
      enough.
    • superseded --replacement <id>: a result that other finished Work replaces. Both patches
      are kept.

    STOP now points to dispatch close <id> --reason already-present instead of reject, so
    changes that landed some other way are no longer recorded as a rejection. reject on
    unfinished Work refuses and names close. In dispatch watch, c closes the selected Work
    after asking for the reason and a confirmation; Enter alone or Esc records nothing. A
    session that ended says so: finish it, resume it, or close it.

    A claude --worktree session's end, which Claude Code reports from the main checkout, is now
    recorded on its Work by session; claude -p sessions report no end, so finish or close their
    Work explicitly.

  • Finish, check and accept say what holds, and what was verified. finish, and a native
    run's result summary, now show the patch, a verdict computed against the source now, and what
    accept would verify:

    Finished, but stale (REFRESH)
    …
    Coherence: REFRESH — 1 file changed underneath
      patch_conflict: the patch no longer applies to c.txt; Git's full output: <run dir>/patch-conflict.txt; …
    
    At accept
      accept would refuse it now (REFRESH), before any merged checks
    
    • The heading follows the verdict: Ready for review, Finished, but stale (REFRESH),
      Finished, already in the source (STOP), Verification failed or Closed.
    • REFRESH and STOP never offer accept.
    • A patch that no longer applies names its paths and keeps Git's full output in
      patch-conflict.txt in the run directory.
    • check adds the same At accept: line.
    • accept prints Merged checks: before it applies anything. Either they passed, with the
      log directory, or they did not run, and it says why: the source has not moved, no checks
      are configured, merged checks are turned off, the Work has no recorded permission to run
      checks on the host, the patch is empty, strict mode, or the configuration could not be
      read. watch's accept notice and the review screen say the same.
    • If the source moves while merged checks run, accept now says nothing was applied because
      the source changed after it checked.
  • clean never removes a workspace live Work uses, and says what it keeps. Before, a
    workspace Dispatch made could be listed for removal while new Work was attached to it, for
    example after repairing it and attaching it again. Now a workspace Dispatch made is cleanable
    only when no unfinished Work, or Work waiting for review, uses its path. Closed results are
    cleanable. Worktrees you or an agent's runtime made are listed as kept, with their Work IDs
    and how to inspect or remove them with Git, and are never removed. "Nothing to clean" no
    longer suggests that every agent worktree is gone.

  • Work that began from older source says so, once. Accept does not commit, so a worktree
    made from HEAD, or from a remote branch, can miss accepted changes or local commits. For
    attached Work that Dispatch did not start from the current source (an existing worktree you
    attach, or a session a runtime reports), Dispatch compares the source with the Work's S0 once,
    right after it records the Work, in both directions. attach, status, explain and watch
    show the result:

    Source     differed from its S0 at 1 path (observed 2026-10-13 03:40 UTC, source HEAD aab43dbb): a.txt (in applied Work 01M4SNJ9XR31N21H3CT04PN7XR)
               Work started with dispatch attach -- <agent> begins from the current source, including uncommitted changes.
    
    • The advice leads with dispatch attach -- <agent>, which works for any agent.
    • Only a Claude Code worktree also gets Claude-specific advice: local commits reach it only
      with worktree.baseRef: "head", and neither that setting nor a commit copies uncommitted
      changes.
    • A path is named as another Work's only when that applied Work's patch contains it.
    • The observation is recorded once and never recomputed. Changes after the start are the
      verdict's business.
    • Runtime-registered Work that no 0.4.12 owner saw recorded, or whose observation
      failed, shows "not observed", and is never filled in later.
    • It is advisory: no verdict depends on it, and Dispatch changes no settings and commits or
      pushes nothing.

Disclosure. Before 0.4.12, an ID-less dispatch accept or dispatch reject with several
results pending for the same source acted on the newest one. It could record a human decision,
and for accept apply the patch, on Work nobody chose. Existing records are not rewritten.

Not in 0.4.12: it does not make claude --worktree inherit uncommitted source, and it does
not replace Claude Code's worktree creation. Discovering agent worktrees without hooks, session
liveness and project setup are planned for 0.4.13. Nothing is refreshed, retried or applied
automatically.

No migration: the schema stays at 24. JSON changes are additive: outcome.closed, the
attachment's source_at_start, the attach.source_observed event, more work.closed
reasons, and the state closed in watch --json, serve --json and history. Scripts
that call accept or reject without an ID should pass one. "Upgrading to 0.4.12" in
docs/release-install.md has the details; restart the owner after upgrading.

Built with Dispatch: four packets, three for the code and one for these docs, each an
interactive Claude Code session that a pinned Dispatch 0.4.11 started in its own workspace
(dispatch attach --root <clone> -- claude), reviewed and accepted by a human one at a time.

Dispatch v0.4.11

Choose a tag to compare

@github-actions github-actions released this 08 Oct 20:46
18a543b

Dispatch 0.4.11 — A clearer dispatch watch

A herdr demo with three Claude Code agents, each in its own worktree, showed that
dispatch watch judged the work correctly but was hard to read. Every Work item was one
sentence of eight to ten fields that wrapped across several lines, with no color. Work was
named only by IDs that differed in one character. The S0 hash shown was a Dispatch snapshot
commit that looked like an ordinary Git commit. If Work left the list, the selection could
silently move to other Work. 0.4.11 redesigns the interactive view only.

  • A table you can read. At 90 columns or more, watch shows one line per Work, with the
    selected Work's details below:

       WORK          AGENT    STATE     VERDICT     CHECKS      TOUCHES
     › me-endpoint   claude   blocked   REFRESH     passed      –
       auth-ctx      claude   applied   –           passed      –
    

    Below 90 columns, each Work is a two-line card. The selected Work's reason and next step
    stay visible down to 40 columns.

  • Work has a name. A row shows the Work's worktree folder (auth-ctx), else its task,
    else a short ID. Duplicate names are told apart.

  • The details explain. The selected Work's details show:

    • its verdict with the full reason, and, for Work still running, that the verdict is
      advisory;
    • its checks;
    • what it touches, naming both pieces of Work ("auth-ctx changes the signature of
      validate (tinyauth/auth.py), which me-endpoint uses");
    • what it began from, saying plainly when that is a Dispatch snapshot and not a commit
      on any branch;
    • what to do next.
  • The selection is the Work, not the row. It stays on the same Work as the list
    changes. If the selected Work leaves the list, the next key only says where the selection
    went. Every action reloads the Work by ID first.

  • Feedback names the Work. For example, "me-endpoint: not applied: stale (REFRESH). The
    source is unchanged. Next: r reject it." Messages age out. Confirmations name the Work
    they act on.

  • Restrained color. Color marks only the selection, an actionable CONTINUE, REFRESH and
    STOP, failed checks, refusals, and landed Work, which is dimmed. The words always carry
    the meaning, and NO_COLOR, --no-color and --ascii work as before.

  • No phantom keys. watch has no auto-apply mode, so it no longer shows Shift+Tab or
    reacts to it, including on its review screen. The hint lists only the keys that apply to
    the selected Work.

Unchanged: piped watch, watch --plain, watch --json, serve, status and every JSON
contract. Every CLI message and all coherence and interaction semantics are also unchanged.

Built with Dispatch: one implementation worker, an independent review and acceptance-test
worker, and a fix packet, each a Claude Code session in its own worktree, supervised by a
pinned Dispatch 0.4.10 and integrated one at a time after human approval.

Dispatch v0.4.10

Choose a tag to compare

@github-actions github-actions released this 07 Oct 18:43
648fbff

Dispatch 0.4.10 — No silent gaps

Building 0.4.9 with Dispatch itself, four agents working at once, exposed two
gaps on a busy machine:

  • a Claude Code session's start hook was killed while Dispatch was still
    registering it, leaving the session untracked and half-made run state behind;
  • dispatch attach failed when a build tool deleted a file while Dispatch was
    reading it.

0.4.10 closes both.

  • A session is tracked, pending, or told it isn't. For a session in its own
    worktree of a watched project, Dispatch finishes registering within 40 s, well
    inside the hook's 60 s timeout. The session is told which of three outcomes it
    got:

    • tracked: its Work exists before the hook returns;
    • pending: its starting state is saved, and the watching owner creates the
      Work moments later;
    • not tracked: its starting state could not be captured in time, or the
      worktree kept changing while it was captured.

    Work is assembled outside runs/ and moved in only when complete, so a
    killed hook never leaves a half-made run behind. A repeated session start
    lands on the same Work.

  • Registration ignores build output. Attaching Work no longer reads the
    integration root's whole tree. Outside strict mode nothing used that walk,
    and it was what made registration slow and fragile: Claude Code worktrees,
    each with its own build directory, live inside the root. Where a walk remains
    (strict mode), a file that disappears mid-walk counts as a difference, not a
    failure.

  • A moving workspace never gives a mixed starting state. If the worktree
    changes while its starting state is captured, Dispatch captures it once more,
    and otherwise refuses rather than record a baseline mixing two moments.

  • Running Dispatch on Dispatch. docs/self-hosting.md is the setup that
    built this release:

    • a pinned, verified release binary;
    • a dedicated state directory outside /tmp;
    • a dedicated integration clone, with project-local hooks;
    • one isolated workspace per worker;
    • landing one contribution at a time.

    It also sets the rule for build caches in checks: a cache may be reused only
    when one workspace's build can never stand in for another's.

Fixed.

  • Notices name the full Work ID. Sessions that started together all read
    "Work 01M4BRE4" in their notice; Work created in the same quarter second
    shares its first 8 characters.

Evidence. A stress trial on a clone of Dispatch, with three cargo builds
putting 1.5 GB of churning build output under the root, every core busy, a
disk write loop, and load averages of 19–25:

  • Six Claude Code sessions started at once: all six were registered by their
    own hooks in 8.0–8.3 s, with exactly one Work each.
  • Hooks killed at 0.05–4 s: those killed before their starting state was
    saved left no Work, and the owner removed their leftovers. Those killed after
    it was saved were published by the owner.
  • The owner killed with SIGKILL while a registration was pending: the
    restarted owner published it.
  • A repeated session start: one Work, the same starting state, both sessions
    recorded.
  • dispatch attach while build output churned: 10 of 10 succeeded, and no
    build output entered any starting state or patch.
  • Two files rewritten continuously during capture: all 6 sessions were told
    they were not tracked. No mixed starting state was accepted.

Building 0.4.10 itself, four workers under Dispatch 0.4.9 all registered by
their hooks. Dispatch reported no interactions between them, which was
correct.

An audit of names the facts layer could not resolve covered about 1,000 names
over six real runs. It found none that hid a meaningful interaction, so binding
is unchanged.

Not built.

  • Strict mode still walks the root's whole tree when attaching.
  • Partial run directories left by older versions are not cleaned up.
  • The hook budget assumes the 60 s timeout dispatch setup installs.

Upgrading. No migration; the schema stays at 24. Run dispatch stop && dispatch start so the project owner is 0.4.10.

Dispatch v0.4.9

Choose a tag to compare

@github-actions github-actions released this 06 Oct 22:27
35d7f61

Dispatch 0.4.9 — Interactions, measured

0.4.8 began reporting where pieces of unintegrated Work touch each other. 0.4.9
records, from real use, how often those reports turn out right, so that nothing
is built on them before that is known.

  • Every landing records what was known. When Work is applied, by accept
    or by auto-apply, Dispatch records the interactions its watching owner had
    reported between it and every other piece of Work in progress
    (interaction.landed). Pieces with no reported interaction are listed too,
    so misses can be counted.

  • The owner records what happened next. The watching owner records its
    next verdict on each of those other pieces (interaction.outcome): CONTINUE
    or not, interaction or not.

    • An outcome is scored only when nothing else could explain it. The landing
      must have been observed, and the other piece analysed and valid before. If
      another change reached the project, or the piece itself changed before it
      was evaluated, the outcome is kept but labelled with why it cannot be
      scored.
    • Each outcome is recorded once, across owner restarts.
  • Documented queries read it. docs/queries/interactions-measured.sql
    gives, from your own state directory:

    • landings by status;
    • outcomes by class;
    • precision (invalidated among predicted) and misses, with their counts;
    • the same by rule;
    • landings whose outcomes are missing.

    docs/coherence-validation.md says what the numbers can support.
    "Invalidated" means Dispatch's own later verdict, not a confirmed conflict.
    Nothing leaves your machine.

  • Live patches are published atomically. A watcher or the owner could read
    a work-in-progress patch while it was being rewritten. Patches are now
    written to a temporary file and renamed.

  • No policy changes. Interactions stay advisory: no verdict, accept or
    auto-apply decision depends on them.

Fixed. A landing auto-applied by an owner on its first tick after a restart
would have seen no interaction view. The owner now compares Work before it
auto-applies.

Evidence. A real-agent trial on a small Python project ran five Claude Code
sessions at once:

  • A changed the signature of auth.validate;
  • B added a caller of it;
  • C changed its body;
  • D changed an unrelated module;
  • E changed another function in the same file.

Before anything landed, Dispatch reported A against B (B uses the signature A
changes) and A against C (both change validate), and nothing else.

D landed first. Its landing was observed, and within 5 s the owner recorded
four outcomes: all scorable, none predicted, all CONTINUE. Then A landed:

  • B: predicted, and REFRESH (fact_broken);
  • C: predicted, and REFRESH (patch_conflict);
  • E: not predicted, and CONTINUE.

The documented queries over that state report 2 landings and 7 scorable
outcomes. Precision was 2 of 2 predicted, with 0 misses among 5 that were not
predicted. That is one small trial, not a rate.

How it was built. This release was built by four Claude Code sessions
working in parallel, each on one packet in its own worktree. Dispatch 0.4.8
watched them, and every packet was integrated through dispatch accept after
review. Dispatch reported no interactions between the packets, which was
correct: none of them shared a file.

The process found two 0.4.8 defects, both still open:

  • a SessionStart hook can be killed by Claude Code's 60 s timeout while
    Dispatch registers the Work under heavy load, leaving the session untracked;
  • dispatch attach --workspace walks ignored build output, and can fail on a
    file that disappears mid-walk.

Upgrading. No migration; the schema stays at 24. Run dispatch stop && dispatch start so the project owner is 0.4.9.

Dispatch v0.4.8

Choose a tag to compare

@github-actions github-actions released this 01 Oct 06:58
29563c6

Dispatch 0.4.8 — Work that notices Work

Dispatch has checked whether Work still holds against the project as it moves.
0.4.8 also shows where two pieces of Work that are not yet integrated already
touch each other, before either lands.

  • Where Work touches Work. While a project is watched (dispatch start),
    Dispatch compares every piece of Work not yet integrated and reports when:

    • both change the same declaration or whole 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.

    This is exact to the declaration for Rust and Python, and to the file
    elsewhere, and it says which.

  • What is not reported: reading the same thing; different declarations of
    one file whose edits keep apart; and a change to a body alone that the other
    only calls. Work mid-edit whose file does not parse for a moment keeps its
    last clean analysis instead of raising a whole-file warning.

  • Where it shows:

    • a dispatch watch row ends with interacts with <id>;
    • the selected row's Concurrent lines say how, for example "it changes the
      signature of validate (auth.py), which this Work uses";
    • dispatch status has a Concurrent section;
    • watch --json and status --json carry a structured interactions list.

    It sits apart from the verdict: Work can be CONTINUE and still interact.

  • Advisory only. Nothing is blocked, reordered, refreshed or stopped. When
    one piece lands, it leaves the comparison and the other is judged by the
    coherence check, as before.

Fixed.

  • A Claude check that timed out no longer refuses the profile for good.
    Under load, claude --version could take longer than its 5 s. Dispatch read
    the missing answer as "Claude CLI version changed" and refused the profile
    until you authorized it again. A claude auth status that did not answer
    was recorded the same way.
    • Now only a contradicting observation is a lasting refusal: a different
      version, a different account, a changed executable, or an expired
      approval.
    • A version check that gives no answer lets the run go ahead: the
      executable's hash, checked first, already fixes its version.
    • An account check that gives no answer refuses that one launch.
  • Work launched together is told apart. IDs are shortened to 8 characters,
    which Work created within a quarter of a second shares. The view and
    interactions now show as much of each ID as tells them apart, and the removal
    hook's suggested commands carry the full ID.

Evidence. A real-agent trial with Claude Code 2.1.280 and Cursor Agent
started five pieces of Work together on one small Python project:

  • A changed the signature of auth.validate;
  • B added a caller of it;
  • C changed only its body;
  • E changed another function in the same file;
  • D changed an unrelated module.

Before anything landed, Dispatch reported A and C changing the same
declaration, and A's signature change against B's new caller. The A-to-B
interaction was already there while B was still running under dispatch attach. It reported nothing for C against B, E, or D.

After A was accepted, it left the comparison within two seconds. The
coherence check then refreshed B (its call no longer matches the signature)
and C (its patch no longer applies), and kept D and E at CONTINUE.

On a 2,000-file repository, comparing 50 pieces of Work costs 1 ms a tick.
Analysing one piece's changes costs about 150–260 ms, and is repeated only
when those changes do.

Not built. Interactions are not stored, not used by any policy, and not
known without a watching owner. There is no ordering advice, no automatic
refresh or stop, no transitive analysis, and no measured precision yet on real
repositories.

Upgrading. No migration; the schema stays at 24. Run dispatch stop && dispatch start so the project owner is 0.4.8.

Dispatch v0.4.7

Choose a tag to compare

@github-actions github-actions released this 28 Sep 15:23
64a2d18

Dispatch 0.4.7 — Work you can finish where you see it

0.4.6 picked up Claude Code sessions as Work by themselves. 0.4.7 lets that Work
be verified, reviewed and cleaned up without you copying run IDs between commands.

  • Checks can run by themselves, when you allow it. dispatch setup --checks
    can let Work in a project run that project's checks by themselves.

    • When Claude Code removes a session's worktree, Dispatch keeps its exact
      changes, as before. Then it runs the checks in a workspace rebuilt from S0 and
      those changes. The Work becomes Ready, or shows its failed checks, and waits
      for your review.
    • The permission names the exact checks.verify commands you approved. If they
      change, it no longer holds until you approve the new ones; dispatch status
      and dispatch watch say which.
    • It is stored in Dispatch's private state, never in the repository, so a
      repository cannot grant itself the right to run commands on your machine.
    • It runs checks only. Nothing is applied without your review.
  • Act from dispatch watch. On a terminal, select a row with ↑/↓, then:

    • f finishes attached Work, asking first before running checks nobody allowed;
    • a accepts, through the same gate as dispatch accept;
    • r rejects, after asking;
    • d or Enter opens the review.

    Each key is exactly the command you would type. watch --json and output that
    is not a terminal are unchanged.

  • dispatch clean. Workspaces Dispatch made for dispatch attach -- <agent>
    are kept after a reject. clean lists the ones whose Work is over and removes
    them, with their branches, only after you confirm. --dry-run only lists them,
    and --yes confirms where there is no terminal. Nothing is ever deleted on a
    timer, and each run's record and patch stay.

Fixed.

  • Accept now runs the merged-tree checks for discovered Work you finished.
    Discovered Work finished with dispatch finish --allow-unsafe-local was accepted
    without its checks running on the merged tree. The flag was not recorded on the
    run, so a change that only the merged tree catches could apply. The authority
    is now recorded, and accept runs the checks.
  • Rejecting Work whose worktree was removed now closes it. dispatch reject
    refused it ("no delivered result to review"), so it could not be closed without
    finishing it. Reject now closes it and keeps its patch in the run.
  • A worktree with no changes that vanished without notice now closes. It
    used to show as lost; it closes with no changes, since nothing was lost.
  • finish no longer fails on a busy project. finish refused the run when the
    background owner was busy with it at that moment. It now waits up to 5 s. Accept
    or reject typed at the command line no longer fails as "stale" because the
    owner had just recorded a verdict.

Evidence. A real-agent trial with Claude Code and Cursor Agent:

  • With consent. A claude -p --worktree session registered Work with local
    authority. Its worktree was removed, and the exact changes were kept before the
    hook returned. The owner then verified the Work by itself: Ready, checks
    passed, nothing applied. After a teammate's commit, it was accepted from
    dispatch watch with the merged-tree checks run.
  • Consent voided. Changing the project's checks voided the consent. The next
    session's Work kept its changes on removal and waited. The trial found that
    rejecting it was refused, which is fixed above.
  • Clean. dispatch attach -- cursor-agent from the checkout finished Ready and
    was rejected. dispatch clean --dry-run listed its workspace, and dispatch clean removed it only after confirmation.

Not built. There is no Codex lifecycle integration: Codex's hooks need a trust
review of every hook definition, and its session end also means "idle".
dispatch attach -- codex remains the supported way to isolate Codex. There is
also no automatic apply for discovered Work, no retention timer, and no start at
login.

Upgrading. No migration; the schema stays at 24. State is forward-only: 0.4.6
cannot read a run finished by consent. After upgrading, run dispatch stop && dispatch start so the project owner is 0.4.7.

Dispatch v0.4.6

Choose a tag to compare

@github-actions github-actions released this 25 Sep 14:11
5e80b64

Dispatch 0.4.6 — Work that appears on its own

Run your agent the way you already do, and Dispatch picks up its work:

  • Claude Code sessions become Work by themselves. With Claude Code's hooks
    installed (dispatch setup → Runtime integrations, shown and approved first),
    a session in its own worktree of a project you watch (claude --worktree)
    becomes Work with no Dispatch command.
    • S0 is the worktree as the session found it, captured before its first edit.
    • Later sessions in that worktree join the same Work.
    • Its verdict follows the project as it moves.
  • Any agent gets its own workspace. dispatch attach -- <agent> run from your
    checkout now makes the workspace. It sits outside the checkout, on its own
    dispatch/… branch, and starts as the checkout's exact world, uncommitted
    files included. The agent needs no worktree support of its own.
    • The workspace is removed once its work is applied, and kept after a reject
      or a crash; dispatch status <id> says where it is.
  • Nothing is claimed where it can't be. A session running directly in your
    checkout is told that Dispatch cannot tell its edits from yours, and nothing is
    tracked.

A removed worktree keeps its work. Claude Code can delete a worktree at
session exit or through its ExitWorktree tool.

  • Before either, Dispatch writes the work's exact final changes durably into the
    run and records the removal. Only then does the hook let the deletion go ahead.
  • If that cannot be done, the hook fails and the worktree stays.
  • Removal never finishes the work for you: dispatch finish verifies it in a
    workspace rebuilt from S0 and the kept changes, or you reject it.
  • A worktree with no changes simply closes.

Fixed.

  • Finishing no longer fails on ignored build output. dispatch finish refused,
    and marked the work failed, when the workspace held over 4 GiB, 200,000 files, a
    file over 512 MiB, or a symlink out of the tree. That included ignored target/
    or node_modules/. Only paths that can enter the changes count now.
  • Process identity on macOS no longer drifts. A process's identity included
    kern.boottime, which the clock adjusts while the machine runs, so live
    processes gradually looked "reused": dispatch stop refused, and a live
    wrapped attach could have been adopted as orphaned.
    • The identity now uses the boot session's UUID.
    • Identities recorded by older versions still match by pid and exact start
      time, so dispatch stop recognises an owner started before upgrading.
  • The project view tells you more:
    • it names where work came from: native, attached, discovered or
      isolated;
    • it says idle, removed, lost and rejected where it said working or
      ready;
    • it stops calling a moved world "unmoved".

Evidence. Real-agent trials with Claude Code 2.1.280 and Cursor Agent:

  • A claude -p --worktree session registered itself, and its Work was created 6 s
    before the agent's first edit.
  • A teammate's commit moved its verdict with no command run.
  • Removing the worktree through ExitWorktree kept the exact changes. The trial
    found that this tool does not run WorktreeRemove, which is why Dispatch now
    also hooks it.
  • The work was then finished in a rebuilt workspace and applied.
  • dispatch attach -- cursor-agent from the checkout worked in its own workspace
    while a teammate committed. It was applied, and its workspace released.
  • A wrapper killed mid-run left its workspace intact, and the owner adopted the
    work.
  • Replayed and resumed sessions never made duplicate work.

Not built. Codex's hooks exist, but every hook definition needs the user's
trust review and its session end also means "idle", so a Codex adapter waits;
dispatch attach -- codex isolates Codex today. There is no process scanning,
automatic verification, start at login, or retention cleanup of rejected
workspaces yet.

Upgrading. No migration; the schema stays at 24. State is forward-only: 0.4.5
cannot read runs that use the new attachment fields. Install Claude Code's hooks
through dispatch setup; install them again if you move the binary or the state
directory.

Dispatch v0.4.5

Choose a tag to compare

@github-actions github-actions released this 24 Sep 23:03
6917f2a

Dispatch 0.4.5 — Dispatch in the background

Turn watching on and get on with your work:

$ dispatch start
✓ Watching ~/src/project
  Dispatch is running in the background. See it: dispatch watch · Stop: dispatch stop

While it runs, Dispatch keeps the verdict of every Work item it knows about current
as the code moves:

  • results waiting for your review, native or attached;
  • attached work still in progress;
  • attached work it may apply.

No agent needs to be set up to watch a project.

New commands.

  • dispatch start watches the project in the background and returns at once.
    It is one local process per project, in its own session, so closing the
    terminal does not stop it.
  • dispatch watch shows the project's Work live, under a line saying who
    watches. Leaving it (Ctrl+C) does not stop watching.
  • dispatch stop stops exactly this project's watcher.

dispatch status now ends with a Project: line saying whether the project is
watched.

Trustworthy lifecycle.

  • "Watched" means the project's lock is held, and the operating system releases
    that lock when the watcher exits, crashes or the machine reboots. So nothing
    claims a project is watched when it is not, and start recovers after a crash.
  • stop signals a process only when its recorded identity (pid, start time and
    boot) still matches.
  • Two starts at once leave one watcher.
  • After a reboot, run dispatch start again.

Fixed.

  • The project owner re-checks results waiting for review when the code moves. A
    Ready result used to keep the verdict it finished with, so the project view
    could say CONTINUE while check said REFRESH. They now agree, and a refusal by
    the merged-tree checks still stands until the code moves.
  • A verdict that flipped back within a minute was never recorded, and a restarted
    serve ignored a stored verdict that no longer held. The owner now compares
    each verdict with the one stored on the run.
  • Attached work with no live owner is re-checked when that work changes, not only
    when the project moves (the gap 0.4.4 closed for native runs).
  • Following attached work used to re-hash the whole workspace every tick. The
    owner now keeps a Git index per Work item, cutting the cost fourfold (about
    50 ms per item on a 2,000-file project).
  • Results waiting for your review stay in the project view until you decide.
    Reasons quoting Git's errors stay on one line.

Evidence. A real-agent trial on a small Python service:

  • dispatch start returned in 0.1 s.
  • On its first tick the watcher found the four results left waiting for review
    that morning and marked them correctly: two REFRESH patch_conflict, one
    REFRESH fact_broken (a caller of validate(token) after the API became
    validate(ctx, token)), and one STOP (already landed).
  • A native Claude Code result and a foreign-attached Cursor session then ran in
    parallel. Two seconds after a teammate's commit touched the same code,
    dispatch watch showed the Claude result as REFRESH, with no command run.
  • After SIGKILL, status and watch said "not watched", stop signalled
    nothing, and start recovered.

Cost (measured on a 2,000-file Git project; no telemetry is collected):

  • An idle tick costs about 50 ms for the project signal, plus about 50 ms for
    each attached Work item in progress.
  • When the code moves, each affected item takes about 0.4-0.5 s to re-evaluate.

Not built. Dispatch does not discover agents it did not launch or that you did
not attach, and it does not start at login. There is still no network service,
protocol or automatic refresh.

Upgrading. No migration; the schema stays at 24. The state directory gains
watchers/. watch --json adds a watcher object, and serve --json is
unchanged.

Dispatch v0.4.4

Choose a tag to compare

@github-actions github-actions released this 24 Sep 17:08
527c2d2

Dispatch 0.4.4 — verdicts you can trust and act on

A multi-agent trial ran eleven Work items, from Claude, Claude Code and Cursor,
against one project while it changed underneath them. Dispatch caught stale work
where plain git apply would have let it through. The trial also showed where its
verdicts could not yet be trusted or acted on. 0.4.4 fixes those places and lets a
human overrule a verdict the analysis got wrong.

Fixed.

  • A refused accept no longer records an acceptance. When coherence refuses an
    accept, nothing is applied, no review is recorded, and the result stays pending
    for you to refresh or reject. Accept now applies first and records your review
    only once the change has landed.
  • Every surface shows the same verdict. After the checks on the merged tree
    refused a result, check and status showed CONTINUE, and check advised accept
    again, while serve showed REFRESH. They now all show the refusal until the
    source moves. refresh passes the failure on to the new agent.
  • Integration failures say what failed. The reason quotes the failing check in
    its own words, for example ERROR: test_admin_token (…) / TypeError: validate() missing 1 required positional argument: 'token', not only a log path.
  • The mid-run watcher also follows the agent's work. It used to re-evaluate only
    when the source moved. A change that landed before the agent touched the same
    code was therefore never reported mid-run. It now re-evaluates when either the
    source or the agent's work so far changes. The work is fingerprinted with the
    trusted baseline repository, so ignored build output never counts.
  • Adding an optional Python parameter keeps callers valid. A referenced Python
    function that gains only defaulted parameters, *args or **kwargs no longer
    marks every caller fact_broken. Any other signature change still does, and
    Rust signatures compare exactly.
  • Clearer words:
    • attached work is told to run its agent again, not to dispatch refresh;
    • native S0 shows the project commit;
    • a result applied to an unmoved source shows unmoved;
    • STOP names the run that already landed the same change;
    • a run waiting for your answer shows question, not working.
  • Answering a question no longer ends the run when the source moved. A native
    run stopped with source_drift when it was answered after any change to the
    project, even though the accept gate judges the result against the source as it
    is then. The continuation now proceeds from the run's snapshot like any attempt.

New: recorded human override. dispatch accept <run> --despite-refresh --explanation "<why>" applies a REFRESH that comes only from the file and symbol
analysis, when you have checked that the work still holds.

  • Your checks must still run and pass on the merged tree.
  • The overridden verdict and your explanation are recorded (coherence.overridden).
  • It never overrides STOP, a patch that no longer applies, or a failing check.
  • Auto-apply never uses it.

Evidence. The same trial was re-run with real agents on this release:

  • The Claude Code patch that still called validate(token) after the API changed
    was refused as before. An override attempt was refused too, because the merged
    tree's tests failed with that TypeError, which the refusal quotes.
  • The redundant timeout change was STOP, "landed by" the run that applied it first.
  • The format change that broke a landed test was refused by the checks, and
    check, status and serve agreed afterwards. The refused accepts stayed
    pending.
  • The Cursor caller of format_user(user) now applies after the optional parameter
    landed; 0.4.3 refused it.
  • Given the failing test, the refreshed agent asked whether it could update that
    test, where 0.4.3's refresh repeated the failure.

Upgrading. No migration; the schema stays at 24. CoherenceRecord gains an
optional overridden field. serve --json gains overridden, and check --json
gains landed_by for STOP.