Neil Camm
← All projects

Dispatch

How I run an AI engineering team as a solo developer.

Role
Creator
When
2026 – Present
Source
github.com/neilcamm/dispatch (opens in a new tab)
Stack
TypeScript · Node · Claude Agent SDK · Codex SDK · MCP · git worktrees · tmux / cmux · Bash (v1)

Claude Code and Codex each run one agent session. Dispatch runs many of them, decides what each one is allowed to do, and checks what they say they did. It started in January 2026 as a 20-line Bash loop and is now a TypeScript CLI I use every day.

Jan 2026 in daily use since

33 deny rules per shell command

2,200+ tests, none call a model

The workers dashboard. A pipeline and a drain are running, and a task worker is paused on a command that needs my answer.

Trust nothing a worker says

  • One chat, one recap. I don't watch terminals. Decisions come to me when needed, and a logged recap when I'm back.
  • Worker output can't steer it. In a worker-triggered turn, the tools that start, merge or grant are shut, apart from a few that code checks first.
  • "Done" is a claim. Each finished task goes through the project's own checks, then a read-only reviewer that can pass, fix or reject.
  • Every result carries a flag. certified, unverified, failed verification or rejected by review.
Coming back after an hour. A task finished certified, a follow-up started on its own, and the credential file waited for me.

Blocked commands pause

  • 33 deny rules on every Claude shell command. git push, git reset, rm, sudo, eval and the network tools, checked part by part, quotes and subshells included.
  • A blocked worker pauses, it doesn't die. I allow it once, by pattern, or deny with a note, and the session resumes.
  • Deletions inside the worktree are settled without me. Anything outside it is refused with a reason. Credential files always come to me.
  • An honest caveat. The guard matches command text. It catches mistakes, not a determined attacker.
One worker's blocked commands. A deletion inside its own worktree goes through, two others are refused with a reason, and the credential file waits for me.

Failure, rule, never again

  • Every failure becomes a rule. Agents read a LESSONS.md of patterns that have actually killed runs before they start.
  • Stalls are caught after two attempts. A drain that once retried a task about 30 times now hands the manual step to me and finishes.
  • The healer is checked too. It can only edit the task file, and a heal that drops work is undone.

Work it finds while I'm away

  • Off by default. Nothing it proposes ships until I approve it.
  • A read-only scout after five minutes idle. Reads issues, feedback, dev server errors, CI failures and run history, and drafts proposals with evidence.
  • One ranked backlog per project. Impact and effort scored 1 to 10, /matrix draws the quadrants, and a score I set by hand sticks.
  • It checks whether the change worked. Each proposal names a metric, with a 30-day baseline and a check 14 days after shipping.
Back from lunch. The scout looked at two areas, kept two proposals and scored them, next to the ideas I added myself.

The pipeline

  • Spec to task file. After an audit of the codebase, so names match the code.
  • Background agents in their own worktrees. Narrow permissions, a turn cap, waves that respect dependencies.
  • Workers outlive the chat. Detached processes, checked against the real pid, resumed after a 529.
  • Merge locally or open a PR. Only when no check fails and the review passes. PRs list every check and a risk rating.
  • A CI watcher. A failing run raises one decision with the job, the step and a log excerpt.
  • Memory it searches. Dated entries, with only pinned rules in the prompt.
  • Claude or Codex. The dispatcher and each worker run on either, through one interface.
The workers dashboard. A pipeline and a drain are running, and a task worker is paused on a command that needs my answer.

Where it stands

  • Rebuilt in TypeScript on the Claude Agent SDK and the Codex SDK, after the Bash version outgrew the language.
  • Over 2,200 tests, none call a model. Fakes for the agents and the GitHub CLI drive a real worker to a real merge on a throwaway repo.

The rebuild decision is in Good Enough Is the Hardest Thing to Replace.

The workers dashboard. A pipeline and a drain are running, and a task worker is paused on a command that needs my answer.