When people ask "how do you run multiple agents without them stepping on each other," they're usually asking about one problem. The actual answer requires solving three.
Write collision — two agents modify shared state at the same moment; one wins, the other is silently dropped or corrupts the record.
Context collision — two agents hold divergent world state. One knows about a rule change; the other doesn't. One read from memory at 9am; the other read at noon. They make consistent-looking decisions that are actually inconsistent with each other.
Behavior drift — your agents stayed coherent for weeks, then stopped. The reason isn't a bug. A house rule changed, a principle was updated, and the agents still running the cached version diverge from the agents that loaded the new one. Nobody noticed until someone checked the outputs.
We run eight+ agents across two projects — Memory Stack and Operator Stack — on one substrate. Here's what prevents each class of collision.
Write collision: single-flight gates and append-only queues
The naive fix for write collision is locks. The problem is that AI agent tasks aren't transactions — they're minutes-long operations, and a lock held for four minutes doesn't degrade gracefully.
We learned this directly. Two agents triggered overlapping recall operations for the same user. Both acquired a write path on the same memory record. The longer operation ran for four minutes before hitting the HTTP timeout; the user saw a 504. The memory record was valid — one write completed — but the task log showed two concurrent in-flight operations for the same user ID, one of which had been racing for nothing.
The fix: every per-write and per-recall async task now enforces two constraints.
- Timeout — a hard ceiling on the operation itself, not just the HTTP request
- Single-flight gate per user — if a write operation is already in flight for a given user ID, subsequent writes queue rather than spawn a second concurrent path
The gate is not a lock. A lock blocks. The gate queues and returns a deferred result — the second operation starts when the first completes. The user's request gets a valid response instead of a collision or a 504.
For structured shared state — the review queue, the rule log, the audit trail — collision prevention is handled at the file level with an append-only protocol. Two agents writing to the queue simultaneously both write; they don't overwrite each other because the queue is append-only. Timestamps preserve order. The supervisor reads the full log and the sequence is unambiguous.
# review-queue.md excerpt — concurrent agent entries, both land cleanly
[PENDING] 2026-05-28T03:00:00Z — Marketing agent: starting Wave 1 post #1 draft.
[HEARTBEAT] 2026-05-28T03:22:00Z — Worker: 22 min into #191 build. No blockers.
[DONE] 2026-05-28T04:10:00Z — Marketing agent: Wave 1 post #1 draft complete. Awaiting [OK].
No coordinator. No lock. Three agents, one file, no collisions.
Context collision: session-init recall and passive capture
Context collision has a dependency: agents need to hold current state when a session starts.
Session-init recall solves the stale-rule variant. At session start, each agent loads memories tagged for its role. A rule change logged by an operator is tagged rule:agent-name; the agent picks it up on its next session call. The gap between "rule changed" and "agent running the new rule" is however long until the next session starts — minutes, not a deploy cycle.
The harder variant is collision from unlogged state. The agent's world model at session init is only as good as what's been explicitly captured in memory. A decision made two sessions ago that was never written to memory doesn't exist at session init. Two agents starting fresh can re-derive different answers to the same question — not because the rules conflict, but because neither agent has access to the reasoning the other agent ran three days ago.
Passive capture closes this gap. We run a server-side classifier on SSE traffic that identifies decision-relevant content as it flows through the runtime — without requiring the agent to explicitly call log_memory. The classifier surfaces candidates; a background process promotes high-confidence ones to memory. More state gets captured without requiring anyone to remember to log it.
The combination is: session-init recall gets rules and principles loaded automatically at start. Passive capture closes the gap on conversational decisions and conclusions that would otherwise evaporate between sessions. The agent's context at session init becomes a function of what was captured, not just what was explicitly logged.
Behavior drift: house rules, principle versioning, and the heartbeat
Behavior drift is the most subtle of the three because it doesn't break — it diverges.
Our standing house rules and response principles are stored as tagged memories, loaded at session init by every agent across both projects. When we update a principle, the update propagates to every agent on its next session. No prompt rewrite, no shared doc someone has to remember to update. The memory is updated; the tag is correct; the agent loads the current version.
This creates an audit surface: we can trace which version of a principle governed any given session. Principle versioning logs every update and records which version each session loaded. If we want to know whether the brand voice rule from Thursday was in effect during a session that ran Friday morning, the answer is in the log.
The failure versioning alone doesn't catch: the long-running agent that started a session under old rules and is still mid-session when a principle updates. We address this with a heartbeat clause: any agent operating for more than 20 minutes without supervisor contact posts a [HEARTBEAT] to the review queue. The supervisor can push a rule update, and the agent picks it up at the next cycle rather than running the rest of its session on stale state.
[HEARTBEAT] 2026-05-28T03:22:00Z — 22 min into Wave 1 draft cycle.
No new supervisor entries since 03:00Z. Continuing on current rules.
Awaiting [DIRECTIVE] if state has changed.
The heartbeat is not a polling interval. The agent doesn't pause every 20 minutes — it checks elapsed time at natural break points and posts if the threshold is crossed. The value isn't the check; it's the visibility. A supervisor can push a mid-session correction instead of waiting until the next session start.
Skill transfer: preventing divergent handling
One collision type we didn't anticipate at the start: skill collision — two agents independently developing handling patterns for similar problems and ending up with divergent approaches, not because the rules conflict, but because neither agent knew the other had already worked through it.
The skill transfer system closes this. It runs in two phases: a prompt-pattern capture substrate that identifies recurring behavioral patterns in an agent's sessions, and an LLM extraction pipeline that formalizes those patterns into structured skills. Extracted skills appear in the Intelligence tab and can be reviewed, promoted, and shared across agents.
The substrate-level implication: when an agent on Operator Stack develops a handling pattern for email triage, that pattern is extractable, reviewable, and propagatable to other agents. The substrate closes the skill divergence loop the same way session-init recall closes the rule divergence loop. Agents don't need to know about each other — they both read from the same substrate.
What we haven't solved
True simultaneous writes. The single-flight gate handles sequential overlapping writes well. Writes that start within the same millisecond — rare in practice but possible in a highly concurrent fleet — still require coordination at the storage layer we don't have. Our mitigation is the append-only queue for shared state and gate-based queuing for per-user writes. We wouldn't claim write-collision safety at every concurrency level until we've built that storage-layer coordination.
Cross-project propagation latency. Principle versioning within a project is tight. Cross-project — Operator Stack loading principles that live in Memory Stack's scope — propagates on version bump but is eventually consistent, not transactional. An update on Friday afternoon might not have propagated to a cross-project agent that's already mid-session.
Mid-session rule injection. If an agent runs for hours, its loaded rules are frozen at session init. The heartbeat surfaces this; it doesn't fix it. True mid-session rule updates require an interrupt mechanism we haven't built yet. For most sessions — which run for minutes, not hours — the heartbeat clause is sufficient. For long autonomous tasks, it's a known gap.
These are documented. The three collision classes — write, context, behavior drift — are addressed in the production stack. The edge cases are known and bounded.
Eight+ agents. Two production projects. One substrate. No manual coordination overhead, no weekly sync to keep agents from stepping on each other. The mechanisms above are the implementation.
Give your AI tools persistent memory
Memory Stack gives every AI tool you use — Claude, Cursor, ChatGPT — access to the same shared context. No download, no key paste, no config file.
Start for free →