The easiest way to build a multi-agent system is to let one agent call another and pass along the conversation so far. It works in a demo. It fails in production for a reason that has nothing to do with model quality: the transcript is the one thing that does not need to cross the boundary, and the two things that do need to cross it are usually missing.
What has to transfer is a task the receiver can accept or reject, and a bounded set of actions it is permitted to take. Everything else is either recoverable from a system of record or should never have left one.
So we ask a blunt question before writing any handoff: after this transfer, who is responsible for advancing the task? If the answer is the receiver, it is a handoff and it needs a contract. If the coordinator keeps the plan and only wants an answer back, that is a tool call wearing a handoff's clothes, and naming it a handoff hides where accountability actually lives.
The envelope we send is deliberately small. Objective, with a test for what counts as done. Current authoritative status. Evidence by reference: record identifiers and a retrieval path, not copied rows. Permitted actions, with limits and an expiry. A return schema. What we do not send is the transcript, the receiver's persona prompt, or the transferor's credentials. The receiver gets its own audience-bound credentials from the platform. A bearer token sitting in a handoff payload is a permission escalation with extra steps.
Separating facts from inference inside that envelope matters more than we expected. A summary that flattens "the customer said" and "the billing system returned" and "the previous agent concluded" into one paragraph of prose lets a guess get promoted to a fact at every hop, and nothing downstream can tell that it happened. We carry the class of each claim next to the claim, along with when it was retrieved, so a receiver can refresh anything stale before acting on it.
The failure that costs the most rework is subtler: treating natural language as a terminal state. An agent that reports "I've taken care of that" has not given the workflow anything it can branch on. Terminal states have to be enumerated and structured: completed, failed, cancelled, input required, approval required, timed out. A failure has to say whether retrying is safe and which effects may already have landed. Otherwise the coordinator's only recovery move is to run the work again and hope every step of it was idempotent.
Two more things we now write down at the start. Non-delegable decisions, such as releasing money above a threshold or accepting legal terms, where the specialist returns an approval request describing the proposed effect and the server re-checks that request after approval, because an approval is evidence about one specific action rather than a standing permission. And ownership of the user relationship during the transfer, because if that is not stated, both parties assume the other one sent the delay notice.
None of this makes the agents smarter. It is ordinary distributed-systems hygiene applied to a boundary that happens to have a model on either side of it, and the tests are the familiar ones: missing fields, expired authority, unsupported schema version, duplicate delivery, and a handoff chain that bounces between specialists because nobody owns the terminal decision.
The full write-up on our own site covers the rest: the field-by-field contract table, what acceptance and rejection look like, the failure matrix for stale evidence and duplicate handoff IDs, and how to trace a handoff as a linked responsibility span.
Agent Handoff Contracts: What State, Evidence, and Authority Should Transfer
Written by the engineering team at Edilec.
Top comments (0)