Appearance
How GAIA works
You'll learn how the three moving parts — control plane, conductor, run — hand a ticket to an agent and get the result back.
GAIA has a control plane that owns the tickets, a conductor that claims them, and a run — one agent session working one ticket state in an isolated worktree. Read it as a loop: the conductor reads dispatchable tickets and starts a run; the run — not the conductor — writes back what it did. The board humans watch is a view of the same data, never a second copy.
The control plane
The control plane is a Drupal application. It stores the canonical domain model — projects, tickets, runs, comments, labels — and it is the only authority for a ticket's state.
Each ticket carries a workflow (gaia_feature, gaia_bug, gaia_chore, and two operational ones) that selects a code-defined state machine. The three work workflows start in qualification, end in done or cancelled, and mark which states a conductor may pick up. The states, their meaning and who acts in each are on The ticket lifecycle.
Claimable is the whole dispatch contract. A conductor may only pick up a ticket whose current state is claimable, whose blockers are all terminal, and which no live claim already holds. Terminal states are never dispatched, so a finished ticket cannot be picked up again by accident.
Reads go out over JSON:API. Writes come back in over the same API — from agents, and from you through gaia ui or the gaia dropsh shell. Nothing writes to the database behind the control plane's back.
The conductor
The conductor is what gaia conductor start runs: a claim-and-dispatch loop bound to one project checkout, tracked in a local registry. There is no central daemon and no TCP — start one per checkout, and the control plane coordinates them.
Each tick does roughly this:
- Heartbeat its registration (last seen, current load) and read its own status.
- Stop here if it is draining, or if its concurrency slots are full.
- Ask the control plane to atomically claim the next eligible ticket.
- Ensure the workspace for that ticket — a git worktree on the ticket's branch.
- Start the agent session detached and mark the run running.
Eligibility is project plus the conductor set on the ticket: a conductor claims the tickets of its project that carry it in their conductor_id, in a claimable state (narrowed further by the conductor's own states list, if any). The one exception is a ticket with no workflow yet — an unclassified import — which any conductor of the project may pick up to qualify. Concurrency limits are per conductor, so cluster capacity is simply the sum of the conductors you run.
What the conductor deliberately does not do: decide implementation content, write ticket state, create sub-tickets or mark work done. Those are the agent's, and they go through the API like any other write. Run a conductor covers the day-to-day.
Runs
A run is one concrete execution attempt at one ticket state, and at the same time the distributed claim lease on that ticket. It records which conductor holds it, when the claim expires, the state it was started for, its worktree and its log. Run states are claimed, running, done, expired and failed.
The run gets an isolated git worktree on its own branch, so two runs on the same repository never share a working tree, and a half-finished run leaves nothing behind in yours.
Because the claim is a lease with an expiry, a machine that dies does not wedge the ticket: a claimed-but-never-started run expires and the ticket becomes claimable again.
Pluggable surfaces
The conductor talks to the outside world through four typed surfaces. Each is filled by an addon — a real npm package you name in the engine config's addons array — so swapping a piece never touches the loop.
| Surface | What it abstracts | Ships as |
|---|---|---|
| remote | the control plane: read tickets, claim, renew, write back | @gaia-ai/addon-remote-drupal |
| executor | where a session runs and how it is hosted | @gaia-ai/addon-herdr |
| workspace | the filesystem the run works in | @gaia-ai/addon-workspace-git |
| agent | which coding agent is launched | @gaia-ai/addon-claude, -codex, -kimi, -opencode, -pi, -grok |
Moving off Drupal would be a new remote addon and nothing else. Running a different coding agent is an agent addon, selectable per project and per ticket — see Choose the coding agent.
Skills and WORKFLOW.md
A state does not just say "coding" — it is owned by a skill, a Markdown instruction file the agent loads. Which skill owns which step is declared by the skill itself and loaded by the repository's WORKFLOW.md. The division of authority is strict: the state machine lives in code, and WORKFLOW.md is plain instruction text — it adds project-specific working rules and the control plane never parses it into states or transitions. That is why a project can change its working rules with a text edit and no migration.
See Skills and the workflow contract.
A state write ends the run
This is the part that surprises people. An agent does not report completion to the conductor. It ends its run by writing the ticket's next state — coding → review, review → done — through the write-back API. The control plane sees the transition and retires the run itself. Release is server-owned: there is no client command that releases a run.
The conductor notices afterwards. Its finalise pass winds up each retired run: capture the transcript and the footprint (tokens, duration) for billing, then close the hosted session. The worktree is not part of that — it belongs to a separate cleanup lifecycle (gaia conductor reap).
Two things follow. The ticket board is always the truth about progress, because progress is a state write. And an agent that dies without writing a state leaves its run open until someone looks — Troubleshooting says where.
Next
- Quick start — see the loop once, end to end.
- The ticket lifecycle — the states and who acts in each.
- The normative spec — the exact contract behind this page.