warden v0.10.1

Orchestration

On this page

Orchestration works through all tickets of an epic: it orders them by their dependencies, runs them side by side, and ships them one at a time.

It needs a ticket tool: a project tool set as the tickets role in .warden/tools/roles.json. See Tools and roles and Write a ticket tool.

Planning an epic from one request#

With the ticket tool's create (required since 0.9), you can ask for something bigger than one ticket in plain words, with /warden:compose or /warden:orchestrate. The agent proposes a breakdown: an epic and its child tickets, each with a type and the earlier tickets it waits for. Once you agree, it creates the epic and then the children with warden orchestrate create (children get --epic and --blocked-by with the ids the tool returned before), commits the changed ticket files on the main branch (with a remote, it pushes the main branch, because runs branch from origin/<mainBranch>; if the push is refused, it stops and tells you the tickets must reach the main branch first), and points you to the Tickets page of warden site (the DAG view) and to /warden:orchestrate <epic>. The ticket tool picks every id. Without create, the agent shows the breakdown for you to add by hand.

Words#

WordMeaning
EpicA parent ticket. Its direct children are the tickets to do.
Ticket DAGThe tickets and their blocked_by links. DAG means a graph with no loops: a ticket never waits on itself, directly or through others.
WaveA group of tickets whose blockers are all done or in earlier waves. Wave 1 can start now.
Held ticketA ticket that waits on an open ticket outside the epic. It gets no wave.
Ticket agentA warden:ticket subagent that drives one ticket's run in its own worktree.
OrchestratorThe main Claude session running /warden:orchestrate <epic>. It starts runs and agents but never does a ticket's steps itself.

Planning#

warden orchestrate plan <epic> asks the ticket tool for the epic's children (dag <epic>), builds the waves, and saves the plan to .warden/state/_orchestrate/<epic>.json. It prints the plan as JSON:

FieldMeaning
ticketsEvery child, sorted by id, with wave, template, phase and run (the live run, if any).
wavesThe open tickets, wave by wave.
nextThe first wave: the tickets to work on now.
heldTickets waiting on an open ticket outside the epic, on a held ticket or on a backlog child, with those blockers.
backlogChildren with status backlog: parked, never in a wave.
errorsProblems that stop the plan: a bad row from the tool, an id that does not match taskIdPattern, a ticket blocked by itself, an unknown blocker, a cycle (shown as cycle: A -> B -> A).
completetrue when every child is done or archived. A closed blocker (done or archived) never holds a ticket back.

The command exits 1 when errors is not empty. warden orchestrate status <epic> prints the saved plan again with each ticket's run and phase read fresh; it does not ask the ticket tool.

Which template a ticket gets#

The ticket's type from the ticket tool decides the flow:

Ticket typeTemplate
bugbugfix
chore, docschore
no type, feature, story, user story, product backlog item, task, enhancementfeature
anything elsecompose: the orchestrator writes a flow that ends with a ship step

Phases#

Each ticket has a phase, from its ticket status and its run:

PhaseMeaning
todoNo run yet.
runningThe run is active at an agent step or a gate.
fixThe run is at a fix step.
readyThe run reached its ship step and no gate is running.
waitingThe run waits for a person (an approval, a fix merge).
blockedThe run is blocked.
pr-openShipped with a pull request that is not merged.
mergedShipped and merged.
no-shipThe run ended without a ship step.
doneThe ticket's status is done.

The orchestration loop#

Diagram source (Mermaid)
flowchart TD
    pull["pull the main branch"] --> plan["warden orchestrate plan epic"]
    plan --> start["start a run for each todo ticket of the wave, one at a time"]
    start --> work["ticket agents work in parallel, up to 4 at a time"]
    work --> fixes["hand fix steps to the fixer"]
    fixes --> work
    work --> ship["ship ready tickets one at a time, in wave order"]
    ship --> mark["pull, then set merged tickets to done"]
    mark -->|wave clean| pull
    mark -->|something needs a person| stop["stop and show what to do"]

/warden:orchestrate <epic> runs this loop:

  1. From the main checkout, pulls the main branch (git pull --ff-only). If that fails, it stops.
  2. Plans the epic. It stops on errors. On the first plan of the conversation it shows a table (wave, id, title, template, phase) and asks you to go ahead.
  3. Starts runs for the todo tickets of the first wave, one at a time: warden start <id> --template <template> --worktree. Each ticket gets its own worktree, branch and run.
  4. Works in parallel. It starts one warden:ticket agent per todo or running ticket, up to 4 at a time, always in the foreground (a background agent cannot ask for permissions, so its commands would be denied). Each agent drives its run until it reaches the ship step, a fix step, or something a person must do, then replies with one line such as READY PG-012 or BLOCKED PG-013: <message>.
  5. Hands fix steps to the fixer. For each ticket in fix, the orchestrator gives the step to a warden:fixer agent and passes the result to warden done. Then the ticket works on.
  6. Ships one at a time, in wave order. For each ready ticket, a ticket agent in ship mode first merges the latest main branch into the ticket branch, fixes any conflicts, and then lets warden run the gates again on the merged code. Only then does it ship. If a ticket does not end merged, no more tickets ship in this call.
  7. Marks merged tickets done. After pulling the main branch, it runs warden orchestrate set-status <id> done and commits the changed ticket files on the main branch. It never pushes the main branch.
  8. Stops when any ticket of the wave is blocked, waiting, pr-open, no-ship, still running after 3 agent runs, or asked a question. It shows a table with each ticket, its phase, the reason, and what you do: type retry <id>, type approve <id>, merge the PR, or answer the question. Otherwise it goes on with the next wave.
  9. When the epic is complete, it sets the epic to done, runs warden clean to remove the shipped tickets' worktrees and merged branches, and shows a final table with each ticket's PR or merge commit.

Run /warden:orchestrate <epic> again to go on after a stop. The saved plan and the live runs pick up where they were. Session start also reminds the agent: warden: orchestration of <epic> stopped with <n> tickets left.

Why shipping is one at a time#

Each ticket was checked and reviewed on its own branch. Once another ticket lands, that is no longer the code it will merge into. So the orchestrator ships in order, and each ticket first merges the main branch as it is now. Warden then runs the gates again on the merged commit, because the proofs no longer cover HEAD. Every ticket is gated on the code it actually lands on.

Rules for the orchestrator and ticket agents#

  • Ticket agents never ask you directly and never run warden ask. When a person must decide, the agent replies ASK <id>: <question>, and the orchestrator brings the question to you. Your answer goes back to the agent as a Note.
  • The orchestrator trusts warden orchestrate status, not the agents' replies (except ASK).
  • The orchestrator never edits files in a ticket worktree and never runs checks.
  • The Stop hook does not keep an orchestrating session going, even when its folder is inside a ticket worktree.

Commands#

CommandWhat it does
warden orchestrate plan <epic>Builds and saves the plan (asks the ticket tool).
warden orchestrate status <epic>The saved plan with runs and phases read again. Exit 3 if there is no saved plan.
warden orchestrate set-status <id> <status>Runs the ticket tool's set-status in the main checkout. Status is backlog, todo, doing, review, done or archived. Prints {id, status, changed}.
warden orchestrate create --title <t> --type <type> --body-file <path> [--epic <id>] [--blocked-by <id,id>]Runs the ticket tool's create in the main checkout and checks the id it picked. Prints {ticket, changed}. Exit 3 with the ticket tool has no create when the tool has none.

All of them exit 3 when no tickets role is set.

At ship, a run whose ship output has a pr also gets the ticket's optional link <id> <pr> (0.8), once, in the main checkout. A tool without link is skipped; a failure is a warning and never blocks the ship.

See also#

Type to search titles, headings and text.