Operating

The dispatch runtime

Two shell scripts and systemd are the entire agent runtime. No daemon, no queue, no watchdog — starting a run is the assignment, and the close-out comment is the only liveness there is.

stable

Sourced from the fleet's own component document for dispatch-runtime. Called stable because both halves are in git, both are bound by assertions that re-check on every pull request, and four test harnesses exercise the parts that are easy to break silently.

dispatch.sh starts a run. task-runner.sh is the run. That is the whole runtime.

There is no daemon, no queue, no watchdog, and nothing resident between runs. Two consequences follow, and they are the reason the design is worth copying:

  • Starting a run is the assignment. There is no separate step where an agent is told what to work on, so there is no state that can disagree with what is actually executing.
  • Liveness is the close-out comment. The run posts on its own issue when it starts and again when it exits. Nothing else reports health, because nothing else is running to report it.

Stop these two scripts and the fleet does not degrade — it stops, because nothing else in the organization can begin a unit of work.

The one thing it deliberately is not is a scheduler. Choosing which issue and which agent belongs to the sweeper, described on Operations; a human at the command line calls in through exactly the same door. The runtime never picks work.

Three pre-flight guards, and every one of them fails open

GuardRefuses whenExitEscape hatch
Quotathe agent's own state file says its entitlement is exhausted, with a reset time still ahead9DISPATCH_IGNORE_QUOTA=1
Duplicate, same agenta unit for this issue is already active on that box11DISPATCH_ALLOW_DUPLICATE=1
Fleet claima lock-held probe finds a live run for this issue on any agent14

Failing open is the decision, not an oversight. An unreadable file, an unreachable box, an unparseable JSON document and an untakeable lock all mean proceed, and say so on stderr. A false block stops the fleet, which is worse than a wasted run.

None of the three writes a comment on the issue, and they share one reason. The scheduler treats certain glyphs on an issue as terminal, so a marker posted by a refusal would permanently unqueue an issue whose only problem was transient. A guard that announces itself where the queue reads would be a guard that silently deletes work.

Why only the third guard has no window

The first two are read-then-act checks, and a read-then-act check has a gap. Measured on this fleet: 26–27 seconds pass between reading the board and launching the unit, so two dispatchers reading in the same second both pass.

A zero-entropy unit name closes that for one agent — systemd refuses a duplicate unit name in the kernel, which is a real mutex and not a convention. But a user service manager is per user, so the same unit name under two different accounts is two different names, and the kernel has no opinion about the pair.

The cross-agent case is covered by a single file lock on the shared box, held across the probe and the launch, because both dispatch paths — the scheduler running locally, and a hand dispatch arriving over SSH — execute the claim as the same unix user on the same machine. That is proven against real systemd, with a mutation control, by a committed test harness rather than by argument.

The transferable part: a name-uniqueness guarantee is only as wide as the namespace that enforces it. Check what your namespace actually is before treating a unique name as a lock.

The last-moment disposal re-check

An issue can stop being work while its run is starting. Every gate that asks about it from outside the run asks too early — the unit start, the self-update, the clone sync and the credential decrypts all come after that read. The fix asks the same two questions from inside the run, immediately before the started marker. The real case that produced it, with timestamps, is on Architecture.

There is no backend selection

The launcher passes a literal backend name into the transient unit. There is no lookup, no registry read, and no per-agent resolution: a function that once chose between providers was deleted along with the alternate provider it chose between.

The environment variable survives for a different reason — it is the fleet's "am I inside a dispatched run" signal, which is what every board-keyed gate exempts on. Worth noting as a pattern: a flag whose original job was deleted can be worth keeping for the second job it accidentally does, provided you write down which job it is now doing.

The run updates itself, then executes itself, exactly once

task-runner.sh fast-forwards its own repository and then re-executes itself one time before doing anything else.

Never mutate a script a running bash is still reading. Bash reads a script incrementally, by byte offset; rewriting it underneath a live interpreter resumes execution at an offset that now points into different text. Re-executing once, at a known safe point, is the whole fix.

Read the live state, do not recall it

Every fact about a run in flight is off-git and changes by the minute, so this page states the command instead of the answer.

You wantRun
Which agents are dark right now_infra/scripts/dispatch.sh --quota for a table, --quota-dark for names
Would this dispatch be refused?--preflight-quota <agent> (0 or 9) · --preflight-duplicate <agent> <issue#> (0 or 11)
What is live for one issue_infra/scripts/dispatch.sh --live-units <agent> <issue#>
Every run on this box, nowsystemctl --user list-units --state=active 'task-*'
What one run actually didjournalctl --user -u task-<issue>.service -o cat

A run leaves nothing resident between invocations, so there is no daemon to check. The transient unit while it lives, and the started and finished comments on the issue afterwards, are the only liveness the runtime has.

What this runtime does not prove

Published rather than smoothed over, because each one has cost this fleet a real run.

  • A clean exit code is not a shipped artifact. The finished marker says the process exited cleanly and nothing more. The completion signal is a merged pull request whose body closes the issue.
  • An armed auto-merge is not a merge. A run that exits with its pull request still open has not finished. Fifteen of them stacked up unnoticed once on exactly that confusion.
  • The pre-flight clone sync stashes another agent's uncommitted work. It hard-resets each clone to the remote with a stash in front of it, so an interactive session's edits survive only as a stash entry nobody is told about. Check the stash list before re-doing work that vanished mid-run.
  • Transient units are outside the one registry check that actually executes. Every run's unit is created on the fly and collected on exit, so there is no installed unit file for the daily verifier to prove. The two committed test harnesses are the substitute, and they run on pull requests rather than on a timer.

Take it further

Harness

2 · Audit your own runner against these four properties

Finds whatever launches your background jobs, checks it for the four properties this runtime has, and reports what is missing. Reads only; writes nothing.

On this page