- Compare the shapes - the whole trade in one table
- The symptom decides the shape - start here if something is already wrong
- Task or handoff - the choice people get wrong most
- Choosing shapes for a booking flow - one brief, worked through
Compare the shapes
Every shape trades something for control. This table is the whole trade, side by side.
These are not exclusive. One agent can run a task in one phase and hand
off in another.
Choose the native shape
If none of these boundaries exists, keep one agent with a clear prompt and its
real tools. You describe the job; the coding agent should choose the shape and
tell you what it chose. You do not need to ask for a task or task group by name.
A server-directed sequence is dynamic, even when its response calls the field
nextStep. Put that loop in one task that asks the returned question and calls
the real domain tools. If fixed stages surround the loop, those stages can be
tasks in a task group. The server owns its dynamic order.
Let the compiler hold the state
- Do not create current-step, happy-path, completion, or routing variables. Tasks, groups, handoffs, and an external server already hold that state.
- Do not create advance, proceed, dispatcher, or transition tools. A tool does real external or local work; a task, a task group or a handoff moves the conversation.
- Keep a variable only when its value really crosses a boundary or feeds a later tool call.
The symptom decides the shape
A prompt that says “always identify the caller first” is a request. A task group
is a guarantee. A
when: clause naming the dependency is not a guarantee the
same way, but it is cheap: no extra task, no extra prompt site, and nothing
the caller has to be spoken through. See Order steps with the
prompt for the full
pattern, including the tool-level check that catches a task run out of
order anyway.
Reach for the task group when the order is what must hold. Reach for a
when: clause when one value should usually be there before one task runs,
and a stray extra turn asking for it is an acceptable cost.
Task or handoff
The difference is whether control comes back. That is why they are two kinds of list rather than one with a kind field: which list a thing is written in is what it is.Order the task, not the route to it
Say the dependency on the task itself, in its ownwhen::
Choosing shapes for a booking flow
A salon booking agent runs into all four decisions in one package. Each lands on a different shape, and together they areexamples/salon-concierge.
Look up services and answer questions. One job, one set of rules: a single
agent with tools handles it. concierge calls look_up_salon_info directly,
with no task in between.
Confirm who is calling before anything personal. One job with a definite,
typed answer: a task. verify_customer holds the one tool that looks the
caller up, ends on that tool through finish:, and saves customer_phone
with assign:. The customer_phone variable carries
confirm: verify_customer, so until this task has heard the caller agree, the
number reaches no other prompt and every tool that needs it refuses. The task
keeps a when: of its own for one case only: the caller correcting their phone
number. Every other route into verification goes through the group below.
Does identifying the caller have to come before booking, every time? That
is the task-group question, and here the answer is yes. A booking made against
a number nobody agreed to is a booking for a stranger, so the order is not a
request to the model. It is something the package has to hold. The group
book runs verify_customer and then manage_booking, with
context_scope: shared so the booking step hears what verification heard, and
then: return so the concierge gets the call back:
- The second booking on a call skips verification.
skip_when_confirmed: customer_phoneskips the first step when the number is already confirmed, which is every booking after the first. - The concierge makes one call into the group instead of choosing between two tasks. Which step runs, and in what order, is the group’s decision.
manage_bookinghas nowhen:of its own, because the group decides when it runs. A task an agent runs on its own needs awhen:. A step inside a group does not.
to_complaints
moves the caller to complaint_specialist, who alone holds record_complaint
and alone reads the refund knowledge base through look_up_refund_policy.
Recording a complaint is one action, not an ordered step, so it sits directly
on the specialist rather than behind a task. to_concierge carries the
reverse direction. The specialist does not list verify_customer: only the
concierge verifies, and every tool that needs the number refuses while it is
unconfirmed.
What is left over goes to a person. A caller who asks for a manager reaches
to_manager, a cold transfer, not any of the four shapes above. Both agents
hold it, so asking for a person is never gated on identifying yourself first.
Where to go next
Making a task actually run
Why a task you declared never runs, and how to fix it.
Designing declared state
Choosing what a task saves, and the type each value gets.
Run it locally
The dev loop: ports, logs, and picking a target.
Optimize the build
The settings that cut round trips and speed up a call.