From 430d83453e285f17459494e24f7df9b4f2b435ea Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Thu, 30 Apr 2026 22:05:04 +0200 Subject: [PATCH] test(agent): serialise V2.Core RecoveryPath / EnrollmentOrchestrator / CollectorBaseReentrancy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These three classes intermittently failed under the parallel xUnit pool on a busy box (snapshot/journal IO + Task coordination + Interlocked-gate SpinUntil all timing-sensitive against ThreadPool contention from unrelated tests). Confirmed pattern, not real regressions: each class ran green isolated via --filter. Now decorated with [Collection("SerialThreading")] — same collection as DeadlineScheduler, SignalIngress, EnrollmentOrchestratorMaxLifetime, etc. Verified 5 consecutive full-suite runs green (855/855 each, ~28s). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Orchestration/EnrollmentOrchestratorTests.cs | 4 ++++ .../Orchestration/RecoveryPathTests.cs | 4 ++++ .../Telemetry/CollectorBaseReentrancyTests.cs | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/EnrollmentOrchestratorTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/EnrollmentOrchestratorTests.cs index 57f6fe8c..396e4573 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/EnrollmentOrchestratorTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/EnrollmentOrchestratorTests.cs @@ -18,6 +18,10 @@ using Xunit; namespace AutopilotMonitor.Agent.V2.Core.Tests.Orchestration { + // Persistence-IO + Task-coordination tests are timing-sensitive enough that the + // parallel xUnit pool starves the worker threads on a busy CI box. Serialised + // against the other threading-sensitive classes via SerialThreading. + [Collection("SerialThreading")] public sealed class EnrollmentOrchestratorTests { private static DateTime At => new DateTime(2026, 4, 20, 10, 0, 0, DateTimeKind.Utc); diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/RecoveryPathTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/RecoveryPathTests.cs index c89d0fe6..995139f8 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/RecoveryPathTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/RecoveryPathTests.cs @@ -20,6 +20,10 @@ using Xunit; namespace AutopilotMonitor.Agent.V2.Core.Tests.Orchestration { + // Snapshot/journal file IO + ThreadPool-driven persistence races make this class + // intermittently flaky under the parallel xUnit pool. Serialised against other + // threading-sensitive classes via the SerialThreading collection. + [Collection("SerialThreading")] public sealed class RecoveryPathTests { private static DateTime At => new DateTime(2026, 4, 20, 10, 0, 0, DateTimeKind.Utc); diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Telemetry/CollectorBaseReentrancyTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Telemetry/CollectorBaseReentrancyTests.cs index e8733eec..f327dfda 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Telemetry/CollectorBaseReentrancyTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Telemetry/CollectorBaseReentrancyTests.cs @@ -28,7 +28,15 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Telemetry /// gate behaviour so a future "let's just remove the guard, it never fires" refactor /// has a hard signal. /// + /// + /// Threading-sensitive: the test exercises the gate via + /// real Tasks + SpinUntil. Under the parallel xUnit pool the ThreadPool gets + /// contended by unrelated tests and the SpinUntil times out before the gate + /// closes. Serialised against the other threading-sensitive classes via + /// SerialThreading. + /// /// + [Collection("SerialThreading")] public sealed class CollectorBaseReentrancyTests { private static AgentLogger NewLogger(TempDirectory tmp)