Skip to content

Stoat as a work queue for a lead agent ​

There are two ways to get a plan worked by agents.

  • The chapter runner (stoat run-plan) launches and drives the workers: it claims each ready node, starts an agent in its own worktree, resumes it when its PR goes red, merges on green where you allowed it, retries a failed attempt, and gives up loudly when the attempts run out. You look in when something calls for you.
  • A lead agent is one Claude Code session you work with. It takes the lead of the plan, and Stoat is its work queue and its ledger: the lead reads what is ready, claims it, does it (or hands it to sessions it supervises), proves it, and asks you when a decision is yours. No runner is involved.

This page is the second way: what it is good for, what it costs, and how to run one.

Why a lead suits novel work ​

The runner is built for well-specified, repeatable work: a slice whose specs say exactly what done is, a version bump, a known kind of fix. There, a failure is usually something an agent can fix, so retrying, budgeting the fixes and giving up after a bound are the right responses.

Work with a lot of novelty is different. A .NET major-version train across a dozen repositories, a new subsystem, a design that is still settling: most of what goes wrong there is a question, not a bug. Which target framework does this package drop? Do we keep the old wire name? Is this test's expectation wrong, or the code? A runner answers a question with an attempt. It spends retries on work that needed a person, its give-ups pile up, and your context is split across a dozen worker sessions, each of which knows one node.

A lead keeps the work in one conversation with you:

  • One conversation, carried across nodes. What you decided on the third node is still in the lead's context on the ninth. You do not explain the same trade-off to five workers.
  • Questions batched and answered in place. The lead asks you when it reaches a decision that is yours, in the session you are already in, and answers its own workers' routine questions itself.
  • The plan changes as you learn. When a node turns out to be two, or a dependency was missing, the lead adds and rewires nodes (add_plan_node, link_plan_nodes, change_plan_node) as it goes, and the plan page shows it at once.
  • What was learned is kept. The lead records decisions and gotchas (record_memory, add_repository_note) and your notes on the plan, so the next session, lead or worker, recalls them.
  • Stoat stays the shared record. What is ready, what is claimed and by whom, what waits on you, every decision with who made it and when, the gates' evidence: all of it is on the plan page, the Agents page and in notifications, exactly as for a runner. A lead does not replace any of that; it uses it.

The trade-offs ​

  • Less parallelism. One lead does one thing at a time, plus whatever it hands to sessions it supervises. A runner with --parallel 4 across five repositories does twenty.
  • Less unattended throughput. A lead works while you are there to answer it. A runner works overnight.
  • The lead's context window. A long train fills it. Have the lead record what matters as memories and notes as it goes, so a fresh lead session (/clear, or a new session that takes the lead) can pick up from plan_status, get_node and recall.
  • You must be around. That is the point of the mode, and its cost.

When to use which ​

A lead agentstoat run-plan
The workNovel, under-specified, likely to change as you learnWell-specified, repeatable, with evidence that defines done
A failure is usuallyA question for a personSomething an agent can fix
Your timeIn the conversation, deciding as it goesLooking in when a callout asks
ParallelismOne lead, plus the sessions it supervises--parallel per runner, a runner per repository
Runs unattendedNoYes, with --watch
MergesYours, on GitHub, in the order the needs panel givesYours under manual review; the runner's under merge-on-green
ContextOne conversation across every nodeOne worker per node, briefed by its prompt
Changing the planAs it goes, in the conversationBy you, on the plan page

They mix. A lead can take the novel repositories of a train while a runner works the routine ones: run stoat run-plan --watch in the repositories where the work is mechanical (or press Start… on their bands), and have the lead take the plan. The runner's workers' questions marked needs: agent (or unmarked, while the lead is alive) go to the lead first, and it escalates the ones that are yours. The lead never claims a node a runner holds, since a claim is refused with its holder's name.

Starting one ​

  1. Wire the repository with stoat setup and start the server (stoat). Start the lead's Claude Code session in the plan's main repository, the one most of the work is in, so its .mcp.json names the Stoat server. Approve the stoat server when Claude Code asks.

  2. Have a plan. Author it with the lead (see Plans and nodes), or import one.

  3. Tell the session to take the lead. In plain words:

    text
    Take the lead on plan net11-train in Stoat (lead_plan, with your session id from
    $CLAUDE_CODE_SESSION_ID). Work it as its lead: read plan_status, tell me what is ready and
    what waits on me, then take the ready nodes one at a time. Claim each with your session,
    do it, prove gates with stoat gate run, report the PR, and release. Ask me before anything
    that is a decision: scope, a public API, a trade-off. When I say "go ahead" or "merge-on-green"
    on a node, relay it with the relay tools, naming me as "Jeremy D. Miller". Record what the
    next session should know as memories. Never merge a PR yourself.

The stoat-coordination-agent-workflow skill teaches an agent this loop; with it installed, a shorter prompt does.

The plan header then shows the lead, the Agents page shows it as a lead row (alive or not, the questions with it, how many it escalated, and the plan's needs), and its claims show on the cards like any agent's. The header's × clears the lead.

The loop the lead runs ​

Take the lead, then read ​

lead_plan(plan: "net11-train", agent: "net11-lead", session: "<its session id>")
plan_status(slug: "net11-train")
next_ready_nodes(plan: "net11-train")
get_node(slug: "net11-train", id: "wolverine-tfm", includeGitHub: true)

lead_plan is refused while another lead's session is alive. Stoat decides whether the lead is alive from the session it observes on the machine, never from what the lead says.

plan_status gives the lead what the plan page gives you: each node's stance (you, broken, held, moving, waiting) with stanceWhy, a waiting node's root blockedBy, its needs from a person, a mergeOrder for a PR waiting on a merge, and the plan's grouped needs with its stalled flag. get_node is one node in full: why it is or is not ready, its brief, its claim and questions, people's notes, and the memories and repository notes that apply. Read those before claiming.

Get the go-ahead before claiming ​

The go-ahead is the runner's checkpoint, and claim_node does not check it: a lead working by hand is trusted to ask. So ask. When you say yes, the lead records it as yours before it claims the node:

approve_for_agents(plan: "net11-train", nodes: ["wolverine-tfm"], agent: "net11-lead",
                   session: "<its session id>", relayFor: "Jeremy D. Miller")

Order matters, because a lead cannot relay a decision on a node it holds a claim on (see Guardrails). A node of yours (kind: person, assignee: person) is never claimable; the lead reminds you of it instead.

Claim, work, report, release ​

claim_node(plan: "net11-train", node: "wolverine-tfm", agent: "net11-lead", session: "<its session id>")
   … the work …
report_node(plan: "net11-train", node: "wolverine-tfm", agent: "net11-lead",
            note: "TFM moved; two packages pinned below 11", pr: 5139)
release_node(plan: "net11-train", node: "wolverine-tfm", agent: "net11-lead")
  • The claim is a lease (30 minutes by default, up to 240 with leaseMinutes), and only report_node renews it. A runner renews its workers' claims for them; a lead must report at least once a lease, or the claim lapses under it.
  • The work is the lead's, or a session it supervises. Subagents the lead starts inside its own session work under the lead's claim. A separate session the lead starts (another terminal, a background claude -p) claims its own node with its own session id, and its ask_person questions marked needs: "agent" come to the lead.
  • pr links the pull request to an issue node however its body is worded. If the issue is only partly done, the PR says Part of #n, and the report says what is left: leftOut: ["the docs"]. The node then reads partial, and its dependents wait until you accept it.
  • Releasing changes no status. The node is done when the issue closes, the version is published, the gate passes.
  • Gates are proven, never claimed. From the checkout, stoat gate run net11-train/wolverine-gate runs each declared step and records it; a lead names the gate, since nothing has set BOBCAT_RUN_TAG for it. See Gates in a run.
  • The lead never merges. Merges stay yours, on GitHub. merge-on-green is carried out by a runner, so without one in that repository nothing merges on its own; the needs panel lists the merges in the order they should land.

Park instead of polling ​

await_dependencies, await_package_version and await_run_completion block until the fact changes, and await_questions until a worker's question routed to the lead arrives. Each is one open request. Each waits 240 seconds by default, under the time an MCP client commonly lets a tool call run, and answers a timeout rather than being cut off: the lead calls again. A lead in a conversation with you can also check between steps: await_questions returns at once with every question already open.

Answer workers, escalate what is yours ​

await_questions(plan: "net11-train", agent: "net11-lead", session: "<its session id>")
answer_question(plan: "net11-train", node: "marten-tfm", questionId: "…", answer: "…",
                agent: "net11-lead", session: "<its session id>")
escalate_question(plan: "net11-train", node: "marten-tfm", questionId: "…",
                  reason: "a public API change", agent: "net11-lead", session: "<its session id>")

A question marked needs: "person" never reaches the lead, and an escalated one is yours for good: it is on the top bar, the strip and in notifications. When you answer an exited worker's question in the conversation, the lead relays it with answer_question and no questionId, naming you in relayFor; the runner holding that worker resumes it with your answer.

Change the plan as it learns ​

The authoring tools are the lead's as much as anyone's: add the node a discovery calls for, link the edge that was missing, put a brief on a node for whoever takes it, file the issue for an unbound node and bind it (bind_plan_node). Plan authoring over MCP never turns on merge-on-green; that is yours. Your own notes on the plan or a node go through the lead with add_person_note, recorded as yours.

Relaying your decisions ​

Every control on the plan page that is a person's has an MCP twin that only the plan's live lead may call. The lead uses one when you have said the decision in the conversation, names you in relayFor, and Stoat records it as yours with the lead beside it: the Activity reads "approved for agents, relayed by net11-lead · Jeremy D. Miller".

You sayThe lead relays
"Go ahead on wolverine-tfm"approve_for_agents
"Merge that one on green" / "back to review"set_merge_policy
"That partial is enough" / "send it back"accept_partial
"Do that one next"expedite_node
"That gate's pass is fine as it is"accept_stale_gate_pass
"Run that gate again"reverify_gate
"Accept that finding" / "reject it" / "sign it off"decide_review
"I did the demo" (a node of yours)mark_person_node_done
"CI is off for marten today"set_repository_ci
"Hold the machine" / "one session in marten"set_session_limit
"Note on the plan: …"add_person_note

The rest are in the MCP reference: set_machine_use, mark_for_cloud, allow_asserted_results, set_agent_cap, set_realize_issues, propose_base_fix, set_gate_stand_in.

Guardrails ​

Each of these is refused, with a test named for it:

  • Only the plan's live lead relays. Any other agent is refused, and so is a lead whose session Stoat no longer sees alive. A gone lead's questions are yours at once.
  • Never on a node the lead holds. A lead holding a claim on a node cannot approve, merge-on-green, accept or expedite it: a worker still cannot approve or merge its own work, even when it is the lead. Get the go-ahead first, or release the node before relaying.
  • relayFor must name a person, and not the lead itself.
  • Only for repositories the plan works in, for a repository's controls.
  • Still a person's alone, with no MCP twin at all: clearing the lead, purging a plan, dismissing a question, and every runner command (start, pause, restart).

The decision is still yours. The relay is a record of what you said, not a way for the lead to decide.

See also ​

Licensed under the Business Source License 1.1.