Agent Collision Detection

When two agents reach for the same function at the same time, Sentinel intervenes before the damage is done.

Why This Exists

The single most common failure mode in multi-agent development is not catastrophic. It is quiet. Two agents, working in parallel, edit the same function. Each agent's change is coherent on its own. When both land, the result is a function that compiles, passes the tests the author wrote, and fails the tests the author didn't think to write.

Git catches textual overlaps. It does not catch semantic ones. If Claude replaces a function body on branch A and Cursor replaces the same function body on branch B, a three-way text merge will surface a conflict only if the literal characters overlap. When the two agents both rewrite from scratch with similar shape — a common outcome of LLM-generated refactors — the merge resolves cleanly and wrong.

Collision detection exists to close that gap. The guarantee Sentinel makes is simple:

If two active agents edit the same logic node within a configurable window, both agents will know about it before either one commits.

No polling the other agent's branch, no hoping the reviewer catches it, no post-hoc apology in Slack.

How It Works

Sentinel tracks collisions at the logic node level, not the line level. A logic node is the AST-level identity of a function, class, method, or top-level block that Aura's semantic engine assigns a stable id to. Renaming authenticate to authenticate_user does not change the node id; rewriting the body doesn't either. Only deletion retires it.

The detection pipeline has four stages:

  1. Registration. When an agent calls aura_snapshot on a file (the mandatory pre-edit step), Sentinel records the agent's interest in every logic node that file contains. The record includes session id, agent kind, and a short intent tag if one has been logged.
  2. Overlap check. Each registration is cross-referenced against the active interest set. If another agent has registered interest in any of the same nodes within the collision window (default 30 minutes), a collision is flagged.
  3. Notification fan-out. Both agents receive a SENTINEL COLLISION event on their next MCP tool call. The event is synchronous — the tool response carries it inline — so an agent cannot miss it by not polling.
  4. Resolution tracking. Once the agents exchange messages via the inbox or one agent releases its zone, the collision is marked resolved in the audit log.

What the notification looks like

A collision event looks like this in the raw MCP response:

{
  "sentinel_events": [
    {
      "kind": "collision",
      "node_id": "py:src/auth/user_service.py::UserService.authenticate",
      "peer_agent": {
        "session": "claude-7f3a2b",
        "agent_kind": "claude-code",
        "intent": "Add OAuth flow to authenticate()"
      },
      "your_intent": "Fix timing attack in password compare",
      "first_seen": "2026-04-21T14:02:11Z",
      "recommended_action": "aura_sentinel_send"
    }
  ]
}

Claude Code surfaces this to the model as part of the tool response, with a short preamble that the CLAUDE.md protocol teaches the model to recognize. Cursor renders it as a banner in the composer. Gemini CLI prints it to stdout before the next prompt.

Why logic nodes and not lines

Line-based detection would miss the most dangerous cases. Two agents rewriting the same 40-line function produce textually different diffs that a line-based checker treats as disjoint. At the AST level, both agents touched the same node, and that is exactly the signal Sentinel needs.

Aura's identity system also means collision detection is rename-proof. If Claude renames authenticate to authenticate_user and Cursor concurrently edits what it thinks is still authenticate, Sentinel sees both agents on the same node and flags the collision, even though the textual path no longer matches.

Resolution Patterns

The tool gives you a signal. The team decides what to do with it. Three patterns cover almost every real case.

Pattern 1: Yield and rebase

The second agent to arrive steps back and lets the first finish, then rebases on top of the completed work. This is the default for small, well-scoped changes.

[Agent B on collision] → aura_sentinel_send to=A
                         "I saw the collision. Yours is bigger —
                          I'll wait and rebase."
[Agent A on completion] → aura_sentinel_send to=B
                          "Done. You're clear."
[Agent B]               → aura_live_sync_pull
                        → resume with Agent A's changes applied

Pattern 2: Merge by conversation

Both agents have changes that need to survive. They exchange enough context to produce a combined plan, one agent executes it, the other verifies.

[A] → "I'm refactoring to OAuth. What's your fix?"
[B] → "One-line: use hmac.compare_digest instead of ==.
       Safe to fold into your refactor?"
[A] → "Yes. Landing with your fix included."
[B] → aura_zone_release; subscribe to A's commit via aura_live_impacts

Pattern 3: Split the node

The function is doing too much. Both agents agree to extract a helper, each owns one half, neither edits the other's new function. This is almost always the right move when the collision repeats.

[A] → proposes: extract verify_password() → B owns
[B] → extract oauth_exchange()            → A owns
      authenticate() becomes orchestration only

Examples

Detecting the collision

From Claude Code, after Cursor has already registered interest in the same file:

User: refactor authenticate() to support OAuth

[Claude]
→ aura_snapshot file=src/auth/user_service.py

← 🚨 SENTINEL COLLISION
   node: UserService.authenticate
   peer: cursor session def456 ("Fix timing attack")
   first_seen: 3m ago

   Recommended: coordinate via aura_sentinel_send before editing.

Claude, following its CLAUDE.md protocol, will now open a conversation with the Cursor session before touching the function body.

Configuring the window

The collision window is the amount of time an agent's registered interest stays active without a commit. Long windows catch more collisions; short windows reduce noise.

[sentinel.collision]
window = "30m"
mode = "warn"               # warn | block | silent
include_callers = false     # flag edits on direct callers too
min_node_kinds = ["function", "method", "class"]

Setting mode = "block" turns collision events into hard refusals — the second agent's aura_snapshot call returns an error, and the pre-commit hook refuses the commit.

include_callers = true is the paranoid setting. It flags collisions not just on the same node, but on any function that directly calls a node another agent is editing. Useful in fragile codebases; noisy in most.

Forcing a collision for testing

When you want to verify the wiring works end-to-end:

aura sentinel simulate-collision \
  --node "py:src/auth.py::authenticate" \
  --peer-agent "cursor-test"

Both the simulated peer and your real agent session will receive the event. Use this once after setup and then never again.

Configuration

The collision subsystem is on whenever Sentinel is on. The knobs you will actually touch are window, mode, and the per-agent overrides from the main Sentinel config. A conservative starting point for a team of four:

[sentinel]
enabled = true

[sentinel.collision]
window = "45m"
mode = "warn"

[sentinel.agents."claude-code"]
collision_mode = "block"     # Claude is authoritative, block others

[sentinel.agents."copilot"]
collision_mode = "silent"    # Copilot is PR-only, don't nag mid-flow

The block setting on Claude here encodes a team norm: if Claude is working on a function, everyone else waits. Teams with more symmetric agent usage leave everyone on warn.

Telemetry

Sentinel logs every collision event to .aura/sentinel/collisions.jsonl with the full node id, both agents' intents, timestamps, and the eventual resolution. This log is how you answer the question "are our agents actually conflicting?" at the end of a sprint. If you see the same node appearing five times in a week, that node is a hotspot and a candidate for Pattern 3 — split it.

Edge Cases

A few situations trip up collision detection in ways worth calling out explicitly.

Rapid-fire edits by the same agent. An agent that calls aura_snapshot on the same file ten times in quick succession should not be flagged against itself. Sentinel deduplicates registrations by session id.

Human commits in the same window. When a human commits a change to a node that an agent is also editing, the agent receives a SENTINEL HUMAN_EDIT event instead of a collision. The semantics are the same — coordinate — but the framing is different because human edits are typically authoritative in a way that agent edits are not.

Stale registrations. An agent that crashes mid-session leaves its registrations behind. The Sentinel auto-expires registrations after two times the collision window (one hour at default settings), and aura doctor can sweep stuck registrations manually.

Cross-repo work. An agent editing the same function under two different checkouts of the same repo is a real scenario for some workflows. Sentinel treats the checkouts as a single logical repo if they share a Mothership peering id; otherwise they are treated as separate fleets.

Noop edits. If two agents both rewrite a function to produce the same AST, the AST-diff phase of the pre-commit hook recognizes the equivalence and the second commit becomes a fast-forward. The collision event was still raised; the eventual resolution was just trivial.

How Collision Detection Interacts With Zones

Zones are preemptive; collisions are reactive. When both are active they reinforce each other.

An agent that claims a zone signals "I am going to produce collision-prone edits here, everyone else stay out." Other agents respect the claim and don't register interest on nodes inside it. Collision events inside a zone are therefore rare, and when they occur they indicate either a zone violation (bad) or a scope miscalibration (the zone was too narrow). Either way it's useful information.

The inverse is also true. Repeated collisions on a node that nobody has claimed are a signal to someone to claim it. If UserService.authenticate shows up in three collision events in a day, the next Claude session that edits it should claim it.

Collision detection tells you where coordination is needed. Zones tell you where it has been arranged.

See Also