Serialize dotnet test to a single writer per host.

Test-serializer (N=1 flock on dotnet test)

Intent — A host-level wrapper that serializes dotnet test to a single writer via an exclusive flock, so concurrent agent worktrees on one machine don't saturate I/O and interfere with each other's test runs.

SummarySerialize dotnet test to a single writer per host.
TargetAgent · Mediators & resource locks
Formregression
EnforcementHard (deterministic) · blocking — exclusive flock, N=1; the enforcer refuses a raw dotnet test from an agent-worktree CWD · bypass ADA_TOOL_TEST_BYPASS_MEDIATOR=1

Motivation — the failure it kills

Several worktrees each running dotnet test on one host saturate CPU and disk and interfere (port contention, shared build artifacts, I/O thrash), so tests flake or hang for reasons that have nothing to do with the code under test. The failure is false-flaky tests plus wall-clock blowup, and it recurs whenever two or more agents test at once, which under a fleet is most of the time. Worse, a false flake sends an agent chasing a non-bug.

Why it's not just "they're separate processes, let them run"

Separate processes still share one host's CPU, disk, and ports; parallel dotnet test contends destructively regardless of process isolation. The serializer acquires an exclusive flock before invoking dotnet test (N=1 writer) and, decisively, a [ModuleInitializer] enforcer inside the test assembly makes the un-mediated path impossible: a raw dotnet test launched from an agent-worktree CWD is refused. The distinction is a mediated single-writer whose raw call is structurally banned versus uncoordinated processes contending for a shared machine. The ban makes the serialization real rather than a convention agents forget under time pressure.

Mechanism

The test serializer flocks a host-global lock, then runs dotnet test; a 30-minute wait cap fails loud if the lock is stuck. TestMediatorEnforcer (a [ModuleInitializer]) refuses raw invocation from agent-worktree CWDs. When the filter contains Fuzz/Campaign, coverage collection is auto-appended. Adjacent heavy tools (build, tsc, pyright, …) route through the sibling build-serializer instead, at M=8.

Prerequisites

Consequences & costs

Known uses

Related mechanisms