Structured Agent Handoffs: Contracts, State Machines, and Acceptance Gates
A delegation message is not a handoff protocol. A reliable handoff binds an objective to scope, permissions, inputs, budgets, acceptance checks, lifecycle state, and evidence. The coding model is replaceable; that contract is the durable system boundary.
I learned this by routing implementation work from an OpenClaw orchestrator to Qwen Code. The early design looked clean on a diagram. The operating history exposed the missing parts.
What the original three-message story missed
The first version described proposal, response, and approval. That reduced ambiguity, but it did not guarantee integration. Main-machine learning records later captured three concrete failures:
| Observed failure | Why the handoff allowed it | Contract repair |
|---|---|---|
| A package compiled but was not wired into the MCP server; the note estimated this occurred in roughly one in five tasks | “Build the package” was treated as completion | Require tool registration, handler, composition-root wiring, and an end-to-end call |
| Registering an MCP server in OpenClaw did not make it available inside Qwen's separate process | Capability availability was assumed across process boundaries | Preflight the assignee's actual tool list and runtime identity |
| Fourteen scheduled jobs were duplicated onto a Qwen/ACP route without needing a coding agent | Routing policy and task intent were conflated | Make assignee selection explicit and reject unsupported scheduled routes |
These are operational notes, not a controlled benchmark. They are still more useful than a flawless anecdote because they identify where the protocol boundary failed.
Separate contract, transport, and executor
These layers solve different problems:
| Layer | Question | Examples |
|---|---|---|
| Handoff contract | What is authorised, complete, and provable? | JSON document, schema, acceptance evidence |
| Transport | How does the request move? | Local queue, SSH, HTTP, MCP tool call |
| Executor | Who performs the work? | Qwen Code, Codex, another coding agent, a human |
MCP's official architecture likewise separates its JSON-RPC data layer from transport. MCP can expose tools and progress, but it does not define your product's delegation semantics, acceptance policy, or authority model.
The contract envelope
{
contractVersion,
handoffId,
idempotencyKey,
createdAt,
issuer: {id, role},
assignee: {id, role},
objective,
scope: {include, exclude},
inputs: [{name, uri, digest?}],
constraints: [],
acceptance: [{
id,
assertion,
verificationCommand?,
evidenceRequired
}],
permissions: {read, write, network, secrets},
budget: {wallClockSeconds, maxAttempts},
escalation: {on, route},
state,
attempt,
result?
}
The downloadable JSON Schema validates this structure. The companion EntityScope example is a reference contract reconstructed from the system's lessons; it is not presented as a verbatim historical message.
Treat lifecycle as a state machine
proposed ──accept──▶ accepted ──start──▶ running
│ │ │
└──cancel───────────┴──cancel──────────┤
├──blocked──▶ running
│ │
├──verify──────▶ verifying
│ ├──succeeded
│ └──failed
└──failed
Every transition needs an actor, timestamp, reason, and attempt number. Terminal states are immutable. A retry creates a new attempt under the same idempotency key; it does not erase the failed evidence.
“Done” is not a state. succeeded means the verifier executed every required acceptance check against the integrated system and retained the evidence.
Preflight before spending tokens
- Resolve the assignee. Confirm the intended agent exists and is appropriate for the task type.
- Negotiate capabilities. Inspect the tools visible inside that exact process, not another agent's configuration.
- Verify inputs. Resolve repository paths and immutable revisions; reject missing or stale briefs.
- Authorize scope. Grant only the required read, write, network, and secret access.
- Validate acceptance. Ensure checks exercise the composition root, not only isolated packages.
- Reserve budget. Set wall time and maximum attempts before execution begins.
If preflight fails, keep the handoff in proposed or move it to blocked. Do not let the coding agent improvise missing infrastructure.
Acceptance must cross the integration boundary
The Qwen integration gap produced code that compiled in isolation but was unreachable in the running MCP server. The corrective checklist has eight points:
- package import;
- application struct field;
- constructor parameter;
- constructor assignment;
- tool registration;
- request handler;
- composition-root wiring;
- tests updated at the public boundary.
This generalises beyond MCP. A new class is not a feature. A passing unit test is not an integrated capability. Acceptance should invoke the same public path the consuming agent will use.
Retries need idempotency and classification
| Failure class | Retry? | Action |
|---|---|---|
| Transient transport interruption | Yes, bounded | Resume or retry with the same idempotency key |
| Missing capability or dependency | No automatic retry | Block and repair preflight |
| Acceptance failure | Yes, if budget remains | Return exact failing evidence to the assignee |
| Scope or architecture conflict | No | Escalate to the issuer or human owner |
| Unknown external effect | No blind retry | Reconcile the effect first |
The idempotency key should represent the intended effect, not the delivery attempt. That prevents a transport retry from creating a duplicate branch, deployment, message, or scheduled job.
Permissions belong in the handoff
Machine separation is useful only when authority is actually constrained. “The coding agent runs elsewhere” does not prove it lacks workstation access, internet access, or inherited secrets. Bind permissions explicitly:
- readable and writable repository paths;
- allowed network destinations;
- named secrets, preferably short-lived;
- permitted tools and destructive operations;
- branch, deployment, and messaging authority.
For remote MCP servers that handle sensitive actions, use the protocol's documented authorization flow; do not treat network reachability as permission.
Evidence returned by the specialist
A useful result contains more than prose:
- changed artifact list and revision;
- acceptance command, exit status, and relevant output;
- unresolved risks and deliberately excluded work;
- external effect receipts;
- links to logs, test reports, and reviewable diffs.
The orchestrator should independently rerun high-value checks. The executor's claim that tests passed is evidence to inspect, not a substitute for verification.