Multi-agent crews
When several agents share a corpus, the value goes up — a reviewer benefits from what a deployer learned — and one specific mistake destroys it.
One bearer per member. Not one for the crew.
Section titled “One bearer per member. Not one for the crew.”The tempting shortcut is a single credential for the whole crew. It ruins the two things a shared corpus is for:
| Shared credential | Per-member credential |
|---|---|
| Audit says “the crew” did it | Says which agent did it |
| Every member has the union of all scopes | Each has only what it needs |
| One compromised member reaches everything | Blast radius is one member’s scope |
| Cannot revoke one agent | Can |
Give each member the narrowest scope, clearance and capability set that lets it do its job. A planner that reads broadly does not need write capability; a deployer with a narrow scope cannot be talked into reading another team’s memory.
Identity has to travel with the work
Section titled “Identity has to travel with the work”The mistake that survives review: an orchestrator holds one client and every member calls through it, so every call authenticates as the orchestrator. The scoping above evaporates and the audit trail records one actor doing everything.
Two ways to keep identity attached:
A client per member. Simplest and most robust. Each member holds its own configured client.
One process, narrowed per call. Where a single process legitimately acts for several agents,
narrow with an actor block:
vd.recall("deploy history", actor={"agent": "deployer", "scope_key": "service:checkout"})Narrowing is enforced by rejection: a scope outside the caller’s authority is a forbidden
fault, and a clearance that widens is refused rather than clamped. So this narrows a real credential
— it does not manufacture authority.
Framework state is where identity gets lost
Section titled “Framework state is where identity gets lost”In frameworks that carry conversation state between agents, the credential is easy to drop on a handoff — the next agent inherits the conversation and quietly reverts to whatever client the orchestrator holds.
Carry the member’s identity in session state, alongside the messages, and re-establish it on every hop. Assert it in a test: run a two-hop handoff and check the second call authenticated as the second agent.
Also watch what each member is shown. Some frameworks default to passing the whole prior transcript to the next agent, which quietly hands member B everything member A retrieved — including memory B’s own credential would not have been allowed to see. Restrict what travels to what the next member needs.
What to share, and what not to
Section titled “What to share, and what not to”Share: conclusions, decisions, lessons, outcomes — written with evidence.ingest and scoped
properly. That is the point of a shared corpus.
Do not share: intermediate scratchpads, plans, or transcripts. They are noise for every other member and they cost tokens forever.
Attribute the writes
Section titled “Attribute the writes”Every write carries the agent that made it:
vd.client.call_tool("evidence.ingest", evidence_args( agent="deployer", # not "crew" items=[...],))“The deployer learned this” is worth far more later than “something in the crew learned this”.