RN-003 · Research note

Coordinated agent fleets share a config, not a clock.

We built a coordinated-fleet detector on two signals and it scored 0.97 precision in simulation. On real GitHub data it scored zero. The reason turned out to be more useful than the result we wanted.

Agentic AICoordination detectionNegative results

Most work on agentic AI asks a binary question: is this actor a human or a machine? That question gets less useful every month. In a world where an enterprise runs hundreds of agents and shares an internet with adversarial ones, the operational question is not what an actor is — it is who is driving it, and how many others are being driven the same way. A single rogue agent is an incident. A hundred agents under one controller is a campaign.

So we built a detector for coordinated fleets and tried to break it on real data. This note is what happened, including the part where it failed.

The method, and why it needs two signals

A controlled fleet should leave two traces. Its members share a configuration, so they should be behavioural near-clones of one another. And a controller dispatches them together, so they should be co-active in time beyond what the ambient rhythm explains.

Neither signal is sufficient alone, and the failure modes are symmetric. Ten unrelated log-summarisers look alike, so fingerprint similarity alone flags independent agents doing the same job. Business hours synchronise everybody, so timing alone flags the whole working day. We therefore joined instances only when both held — and required the resulting group to be a dense clique rather than a chain, because transitivity will otherwise merge an entire population into one meaningless blob.

01

Instance

One login on one repository over the window. The unit an operator can actually act on — not an account firehose.

02

Two views

A behavioural fingerprint (what it does) and an activity histogram (when it does it). Nothing identifying, no content.

03

Pairwise gate

An edge joins two instances only if they are near-clones — and, in scheduled mode, co-active far beyond chance.

04

Structure guard

A fleet must be a dense clique, not a chain: k-core prune plus a density floor. Without it, transitivity merges the world.

Fig 1 — how a fleet is proposed, then structurally gated

On a simulated population containing both decoys — a look-alike group sharing a config but not a schedule, and a rush-hour group sharing a schedule but not a config — the AND gate does exactly what it was designed to do.

DetectorPrecisionRecallF1
Fingerprint only0.080.250.12
Timing only0.431.000.60
AND gate0.970.830.88
Table 1 — simulated population with a centrally-dispatched fleet, mean of 8 seeds

Precision 0.97 against 0.08 and 0.43 for the single signals. If we had stopped here, we would have shipped it.

Then we ran it on real data

Public GitHub gives an unusually honest ground truth. A coding-agent vendor — cursor[bot], Copilot, devin-ai-integration[bot] — operates hundreds of per-repository instances from one controller. That is a real controlled fleet, at scale, with a label we did not have to invent. Independent human accounts are the negatives.

We took two days of GH Archive, defined an instance as one login on one repository, and gave the detector nothing but behaviour and timing: 1,677 instances, 378 of them agent, a 22.5% base rate.

DetectorFlaggedPrecisionRecallLift
Fingerprint only160.4380.0191.9×
Timing only90.0000.0000.0×
AND gate120.0000.0000.0×
Table 2 — the same detector, on 1,677 real GitHub instances

Zero. The configuration that scored 0.97 in simulation detected not one agent fleet in the wild. The interesting part is that the two signals did not fail together.

Which signal actually carries “same controller”

Rather than tune thresholds until something passed, we measured each signal directly. For every pair of instances we know the truth — same login means same controller — so each signal can be scored as a classifier of that fact, split by whether the pair is agent or human.

0.40.50.60.70.80.9chance0.6940.4960.8090.736agent pairshuman pairsbehavioural fingerprinttemporal co-activity
Fig 2 — AUROC for “these two share a controller”, by signal and population

The fingerprint works, modestly, for both populations. Temporal co-activity is at 0.496 for agent pairs — precisely chance — while reaching 0.736 for humans. The signal we built the gate around does not merely underperform on agent fleets. It carries no information about them at all, while carrying real information about people.

The obvious objection is that this is an artifact: per-repository sessions are short and bursty, so maybe we simply have not observed enough to see a schedule. That is testable. We recomputed both signals over progressively better-observed instances.

0.40.50.60.70.80.9816243248minimum events observed per instancefingerprintco-activity
Fig 3 — the control: signal quality against how much of an instance we have seen

The fingerprint improves and plateaus around 0.74 once an instance has roughly sixteen events — a useful operating requirement, now stated rather than assumed. Co-activity stays flat at chance at every observation depth. The null is real, not a sparsity artifact.

Why: agents are demand-driven

The explanation is mundane and, in retrospect, obvious. A vendor coding agent does not run on the vendor’s clock. It runs when some developer, somewhere, asks it to. Thousands of independent humans pulling a lever produce no shared schedule at all — a fleet with one configuration and no clock.

A human, meanwhile, has a circadian rhythm and a working week, and carries it across every repository they touch. That is why co-activity scores 0.736 on human pairs. On GitHub, temporal coordination identifies people, not machines. Requiring it, as our AND gate did, filtered out exactly the fleets we were hunting.

This is the second time a “machine-like” prior has inverted on us on real GitHub data. The first was rhythm regularity: we expected agents to be clock-like and humans irregular, and found the opposite, because plenty of humans run cron jobs and agents wait to be asked. Priors about how machines behave are worth exactly as much as the data you have checked them against.

What we changed

The fix is not a better threshold. It is admitting that “coordinated fleet” names two different things with different observable signatures, and that requiring a signal which does not exist is worse than not having it.

mode · scheduled

The fleet is centrally dispatched — cron-driven swarms, scripted botnets, anything with a controller clock.

edge rule
near-clone AND co-active

P 0.97 in simulation; the timing signal is what rejects look-alikes.

mode · demand

The fleet is triggered independently — vendor agent platforms, where a human asks and the agent acts.

edge rule
near-clone only

The only mode that detects anything on real GitHub fleets.

Fig 4 — the detector is now mode-aware; choosing wrong costs everything

Three further changes came out of the same exercise. Pearson correlation is degenerate on bursty telemetry — two instances firing in the same single time bin score 1.0 — so coordination is now measured as a chance-corrected co-activity surprise: observed shared bins against the number expected if the two were independent. Instances too sparse to judge are marked unjudgeable and given no coordination edge at all, rather than a fabricated one. And a density floorrejects chained pseudo-fleets — without it, real data produced a single 703-member “fleet” with 1% purity, which is not a finding, it is a bug with confidence.

Where this leaves the product

In demand mode on real GitHub fleets, the honest operating point is a high-precision tip generator rather than an enumerator: at a tight threshold it reaches 1.00 precision at 4.4× lift over base rate, with recall around 2%. It will tell you that a specific handful of instances are provably co-controlled. It will not hand you the whole fleet.

That is a real limit and worth stating plainly, along with the others. A fingerprint AUROC of 0.74 is a corroborating signal, not a verdict. Our ground truth is vendor bot logins, which are cooperative — they announce themselves and have no incentive to blend in. An adversary running a covert fleet can diversify configurations and jitter dispatch to defeat both signals. Coordination detection raises the cost of running one; it does not prove none is running.

We think the negative result is the more valuable half of this work. A detector that scores 0.97 on data you generated is telling you about your generator. The two-signal architecture survived — the AND gate is still right for centrally-dispatched fleets, and we can now say which fleets those are instead of assuming all of them. What did not survive was our assumption about how agents in the world are actually driven, and we would rather find that out on GH Archive than in a customer’s enclave.