Home / Learn / The Orchestrator Course / The lane pack and dispatch.sh

Module 5 of 710 min readUpdated 2026-09-10

The lane pack and dispatch.sh

The two to three kilobyte pack that turns a cheap model into a clean run, and the runner that refuses main line branches and runs the tests itself.

  • Free, no signup

The pack got shorter and better

My first packs were long. They carried formulas, background, and every rule I could think of. They produced 2,889 test failures on one unit.

The pack that replaced them is two to three kilobytes and produced 187. Then the 187 turned out to be my own wrong test oracle, not the model. The lesson is measured facts, not formulas. Give the model the number you measured, not the equation you think produces it.

The five blocks

The pack has five blocks and nothing else.

  • Goal. The ticket, the spec section, and one sentence saying what is observably true when it is done.
  • Measured facts. Real numbers from a real run. The current timing, the current counts, the exact identifiers.
  • Task and scope. A file, a function, and a line range, or exact identifiers. Not a description of an area.
  • Runner line. What the agent can and cannot do itself.
  • Repo rules. The rules the diff can silently break.

The one line that changed the most

Every pack carries this, kickbacks included:

You cannot run xcodebuild; the runner runs ./test.sh -t X after you exit.

Before that line, agents burned their whole budget fighting a sandbox that was never going to let them run the suite, then reported confusion. After it, they compile, stop, and report honestly.

The repo rules block

1. Work only inside this worktree.
2. Never switch, create, or delete a branch.
3. Never push, and never open a pull request.
4. Never edit a file outside the scope list above.
5. End your report with `git diff --stat` and the verbatim test output.
6. STOP and write a question if anything in this pack is ambiguous. Do not guess.

Then the domain rules. For my repo those are: keep every L10n.string localization call, and never type an em dash in any line you add.

Naming those two explicitly is the single highest value edit I have made to a pack. On the same ticket, the same model went from deleting two localization calls to deleting none. Another model went from six bare English literals to six localized keys, and its wall time fell from 607 seconds to 381. A rule that lives only in your head is a rule the model will break.

The runner

dispatch.sh --backend opencode|codex --model <id> --worktree <abs dir> \
            --pack <lane-pack.md> --out <result dir> \
            [--timeout-min N] [--test-cmd "<cmd>"] [--dry-run] \
            [--allow-paths <globs>] [--forbid-file <file>] [--codex-review]

The runner is the enforcement layer, not a convenience wrapper.

It refuses a worktree on master, main, ios-main or mac-main, and exits 3. It runs the test command itself, in the worktree, after the agent exits. It writes result.json with a status, the diff stat, the changed file list, and the test exit code. Status is one of ok, backend_failed, timeout, tests_failed, scope_violation or forbidden_hit. Exit 0 means ok, 1 means it ran and failed, 2 is bad usage, 3 is an unsafe worktree.

--allow-paths fails a lane whose diff touches a path outside the declared scope. --forbid-file fails on a forbidden pattern in the added lines. --codex-review runs a cheap Codex self review over a Codex lane diff before the real validator. Never route a diff written by Claude Opus to a Codex reviewer: the measured direction is one way, and it lowers the score.

The trap that cost me an afternoon

Keep --out outside the worktree. An out directory inside it shows up as untracked files to the scope gate, and the old logs from the previous lane trip a scope violation on a lane that did nothing wrong. Mine live under a dot directory in my home folder, one per ticket and model.

Set it up

  1. Write one pack for a real ticket, in the five blocks, and keep it under three kilobytes.
  2. Put your own measured numbers in the facts block. If you do not have one, measure it before you pin it.
  3. Write the scope as file plus function plus line range, not as an area.
  4. Add your repo's own silent breakers to the rules block. Every repo has at least one.
  5. Dispatch with --out pointing outside the worktree, and --allow-paths set to your scope list.
  6. Read result.json first: status, then the changed file list, then the test exit. Read the diff last.

What you can do now

You can dispatch a lane to any backend and get a gated, machine readable verdict without trusting a word of the agent's own report.

Next module

Module 6 is the day to day: session start, worktrees, verifying, kicking back, merging, and the ledger that survives a compaction.

Talk to Claude instead of typing to it.

Ducktate turns what you say into clean text, on your Mac, and pastes it anywhere.

Get early access