← All posts

Tutti: coordinating coding agents with GitHub Issues

A local coordinator that treats GitHub Issues as a state machine for dispatching AI coding agents.

Once you have one coding agent working, the obvious next thought is “what if I had several?” Then you hit the real problem, which is not the model. It is coordination. Which agent runs next, in what order, what blocks what, and when does a human need to step in before something risky lands.

Tutti is my answer to that. It is a local coordinator for coding agents, and it makes one deliberate choice: it does not invent a new control plane. It uses GitHub Issues.

Issues as the workflow surface

Every unit of work is a GitHub Issue, and the state lives in labels. There are a few families of them: state:* for where the work is in the pipeline, role:* for who owns it next, type:* for what kind of work it is, plus priority:* and override:*.

The coordinator polls, reads the labels, decides what to do next, and dispatches the right agent. When an agent finishes, it hands off by flipping labels. The whole workflow is just a state machine where the state is stored in a place I can already see.

A GitHub issues list for a project, with epics and tasks each carrying a state:done label, after Tutti has run the whole thing to completion.
A real project's board after Tutti ran it. Every epic and task is an issue, and the state:* labels are the workflow.

The roles

A fresh issue gets labelled state:triage. A Dispatcher agent classifies it and routes it on. From there, specialised agents take turns: a researcher who writes durable notes into docs/, a PM who turns that into requirements, an engineer, a QA pass, and a merger that actually lands the PR. There are even exploratory roles, like a “monkey tester” that pokes at the product looking for breakage and writes up what it found.

Each role does its piece, writes something durable, and hands the issue to the next owner.

A GitHub issue thread where Tutti's coordinator posts a role run result and an epic-complete summary, then moves the tracking issue to state:done.
The coordinator posts each role run back to the issue and walks it through the state machine. The full logs stay local; only the outcome lands on GitHub.

The parts I actually care about

The interesting engineering is not the dispatching. It is the guardrails:

  • Dependency gates. An issue will not start until the things it depends on are done.
  • Human-verification pauses. At the risky points, the coordinator stops and waits for a person instead of barreling ahead.
  • Retry and loop detection. Agents get stuck and spin. The coordinator notices and stops it.
  • Dry run. I can see exactly what it would do without it touching anything.
  • Explicit overrides. When I need to force past a gate, I add a label like override:deps-ignore. It is deliberate, visible, and easy to clear afterwards.

Why GitHub Issues, and not a dashboard

I could have built a database and a custom UI. I chose not to. Issues already give me a board, comment threads, history, permissions, and a mobile app I already have on my phone. The agents and I look at the exact same surface. When something goes wrong, I am debugging in a tool I already understand, not in some bespoke thing I now also have to maintain.

That turned out to be the whole lesson. The hard, valuable part of multi-agent work is coordination and trust: ordering, isolation, and knowing when to pause for a human. The model is the easy part now.