SwarmScopeGitHub
Agents of SigNoz, Track 01

Make contention between parallel agents observable.

Two agents writing the same file. Six sharing one rate-limited API key. Duplicated identical tool calls. SwarmScope instruments swarms with OpenTelemetry, detects collisions with a sweep line, and ships the whole story to SigNoz.

chaos run collisions
88
Warden active
6
same 6 agents,
same workload
THE PROBLEM

Four failure modes single-agent traces cannot show

Each agent trace looks fine on its own. The bug lives between them, in the resource two agents held at the same moment. That correlation is never in one trace.

write_write

Concurrent same-file writes

Two agents write the same resource at once. One diff silently overwrites the other. Neither trace shows an error.

refactorer and dep-upgrader both write file:src/db.py

read_write

Read torn by a write

One agent reads a resource while another writes it. The reader acts on state that no longer exists a moment later.

tester reads file:src/app.py mid-write

duplicate_work

Duplicated identical calls

Two agents run the same tool with identical args inside a window. Every call after the first is wasted tokens and cost.

linter and security-scanner both run scan_repo(depth=2)

lease_starvation

Lease starvation

An agent waits past the threshold for a lease held by a long-running peer. From its own trace it just looks slow.

an agent blocked > 500ms behind the apikey lease holder

ARCHITECTURE

Three cooperating parts

An OTel SDK that names contended resources, a pure detector that finds overlaps, and a Warden that provisions SigNoz through MCP and heals the swarm.

swarm processagent-1agent-2agent-3..6ledgerOTLP :4318SigNozClickHouse TSDBdashboardsalertsWarden processMCP :8000HealLoopcontrol.jsonspans + metricscreate / readheal: writes .swarmscope/control.json back to the swarm

OTel SDK for swarms

SDK
swarmscope/sdk
  • swarm_run(name) opens a root span and yields a run_id
  • agent(id, role) is a per-agent root in its own trace, span-linked to the run
  • tool_call(resource_key=...) names the thing two agents can fight over
  • ContentionLedger detects collisions live, before either span closes

Contention detector

Detector
swarmscope/detect
  • Pure function: same spans in, same collisions out, no I/O
  • Interval sweep-line, O(n log n + k), not naive O(n squared)
  • Four kinds: write_write, read_write, duplicate_work, lease_starvation
  • Runs online via the ledger and offline over SigNoz query results

MCP-driven Warden

Warden
swarmscope/warden
  • Provisions dashboards and alerts through the SigNoz MCP server
  • Polls SigNoz via MCP and classifies live contention
  • Heals by writing .swarmscope/control.json: leases, dedup bans, concurrency caps
  • Emits a swarm.remediation span so each healing action is observable
INTERACTIVE

Replay the real chaos run, then guard it

Agent lanes down the side, time across the bottom. Each bar is a tool or LLM span. Dots are collisions, colored by kind. Toggle to the guarded run to watch the conflicts disappear. Hover any bar or dot for detail.

run_idrun-7528eeba2361tool calls24collisions88tokens7,208
agent-1refactoreragent-2testeragent-3doc-writeragent-4dep-upgraderagent-5linteragent-6security-scanner0ms250ms500ms750ms1000ms1250ms1500ms1750ms
read spanwrite spanllm (apikey:openai-main)|write / writeread / writeduplicate worklease starvation

88 collisions: 66 write/write, 9 read/write, 13 duplicate

Footnote: the demo persisted only aggregate run totals, not per-span timings. This timeline is reconstructed from the real workload structure (6 agents, 4 tasks, one shared key) and a real half-open sweep-line detector, seeded so the per-kind and per-resource totals reproduce the detector output exactly: chaos 66 write/write, 9 read/write, 13 duplicate (88 total); guarded 6 duplicate, 3 prevented. The headline figures elsewhere on this page are the verified aggregates.

IN SIGNOZ

Genuine captures from the live instance

Traces, metrics, and the before/after all come from a self-hosted SigNoz recording real SwarmScope telemetry. Two dashboards and four alert rules were provisioned through the SigNoz MCP server.

SigNoz Traces Explorer filtered to swarmscope-demo showing many concurrent agent spans in the same millisecond window
Six agents, six traces, one run

SigNoz Traces Explorer, service.name = swarmscope-demo. Concurrent swarm.tool and swarm.llm spans share sub-second timestamps: real multi-agent parallelism, one trace per agent.

A single swarm.llm span expanded showing swarm.resource_key and two swarm.collision span events
A collision recorded on the span

A swarm.llm span with swarm.agent_id, swarm.resource_key = apikey:openai-main and two swarm.collision events attached: agent-2:write vs agent-3:write on the shared key.

SigNoz Metrics Summary listing all SwarmScope custom metrics
Every metric stream ingested

Metrics Summary lists all SwarmScope streams. swarm.contention.collisions leads with 252 samples across 123 time series, recording write conflicts in real time.

swarm.contention.collisions metric detail panel showing run_id, resource_key and kind breakdowns
Collisions broken down by kind

The swarm.contention.collisions detail panel. Both the chaos and guarded run ids sit in the same TSDB, split by resource_key and kind, with two alert rules attached.

Composite of two SigNoz trace-detail screenshots, chaos run with collision events on the left and clean guarded run on the right
Chaos versus guarded, same workload

Left: a chaos-run span dense with swarm.collision events on apikey:openai-main. Right: the guarded run root span with events: [] 0 items. The lease guard eliminated the conflicts.

TELEMETRY CONTRACT

Span and metric names, copied from attrs.py

These strings are the contract between the SDK, the detector, and the Warden. Changing one silently breaks a dashboard, so they live as constants.

Span attributes
attributeconstantmeaning
swarm.run_idA_RUN_IDone id per swarm run
swarm.agent_idA_AGENT_IDstable per-agent id, e.g. agent-3
swarm.agent_roleA_AGENT_ROLEe.g. refactorer, tester
swarm.resource_keyA_RESOURCE_KEYcanonical contended resource
swarm.resource_opA_RESOURCE_OPread or write
swarm.tool_nameA_TOOL_NAMEtool invoked
swarm.tool_args_hashA_ARGS_HASHsha1[:16] of normalized args
swarm.attemptA_ATTEMPTretry attempt, 1-based
swarm.lease_wait_msA_LEASE_WAIT_MSms spent waiting for a lease
swarm.remediation_actionA_REMEDIATION_ACTIONaction taken by the Warden
swarm.remediation_reasonA_REMEDIATION_REASONwhy the action was taken
gen_ai.request.modelGEN_AI_MODELLLM model name
gen_ai.usage.input_tokensGEN_AI_IN_TOKENSinput token count
gen_ai.usage.output_tokensGEN_AI_OUT_TOKENSoutput token count
gen_ai.usage.cost_usdGEN_AI_COSTUSD cost (SwarmScope extension)
Span names
spanemitted by
swarm.runswarm_run()
swarm.agentagent()
swarm.tool <name>tool_call()
swarm.llmllm_call()
swarm.remediationremediation_span()
Metrics
metrictype
swarm.agents.activeUpDownCounter
swarm.contention.collisionsCounter
swarm.resource.wait_msHistogram
swarm.tool.callsCounter
swarm.cost.usdCounter
swarm.tokensCounter
swarm.remediation.actionsCounter
QUICKSTART

One import makes a Python swarm observable

Name the resource each tool call contends over. The SDK, detector, and dashboards do the rest.

instrument a swarm
python
from swarmscope.sdk import agent, tool_call, llm_call

with agent("agent-3", role="refactorer"):
    with tool_call(
        "write_file",
        resource_key="file:src/db.py",   # the thing two agents fight over
        resource_op="write",
        args={"path": "src/db.py"},
        ledger=ledger,                    # live collision detection
    ):
        ...
    with llm_call("claude-sonnet", api_key_id="openai-main") as span:
        span.record_usage(input_tokens=812, output_tokens=240, cost_usd=0.0043)
run the demo
bash
# 1. Deploy SigNoz with the MCP server enabled
foundryctl apply deploy/casting.yaml       # UI :8080  OTLP :4318  MCP :8000/mcp

# 2. Install and provision dashboards + alerts through MCP
uv sync
uv run python -m swarmscope.warden.provision

# 3. Run the swarm: chaos first, then guarded
swarmscope demo chaos      # 6 agents, no coordination
swarmscope demo guarded    # same workload, Warden active

# 4. Filter any SigNoz panel by the printed swarm.run_id
top contended in chaos run:apikey:openai-main63file:src/db.py16file:src/app.py7