Repository navigation
feat(agent-testing): run a suite's cases several at a time - #1465
Conversation
A run executed its cases one after another, so a suite cost the sum of every case's model wait: about 25 minutes for the 39-case suite whose cases each take 30 to 50 s, nearly all of it spent waiting. Cases are independent by construction. Each one opens its own conversation and its own DI scope, and the mock seam, the model override and the tool recorder all find their case by conversation id in a ConcurrentDictionary, never by an ambient "current case" -- so two cases in flight cannot see each other. The executor now runs up to MaxParallelCases work items at a time, taken from the run's own value, else the suite's (default 4, 1 to 16, rejected rather than clamped at the API). What must not overlap is this layer's own bookkeeping: the run document's counts are a read-modify-write of one document, and two of those interleaved lose whichever increment landed first. Each case's tally is therefore taken under a semaphore while only the case executions themselves overlap, which costs one round trip against a case's tens of seconds. Cancelling now distinguishes "stop starting new work" from "the run ended early because of it". A cancel that lands while the last case is executing holds nothing back, so that run still ends in its real status rather than being retroactively Cancelled. Cases already in flight cannot be aborted -- BotSharp's SendMessage takes no CancellationToken -- so they finish and record instead of losing work already paid for. The run records the concurrency it ran at, because a latency percentile from a four-at-a-time run is not the same measurement as one from a serial run, and the executor logs one line per finished case: interleaved cases make progress unreadable from a case's own log lines. Co-Authored-By: Claude Opus 5 <[email protected]>
PR Summary by QodoRun agent test cases concurrently with configurable limits
AI Description
Diagram
High-Level Assessment
Files changed (7)
|
|
reviewed |
A run executed its cases one after another, so a suite cost the sum of every case's model wait: about 25 minutes for the 39-case suite whose cases each take 30 to 50 s, nearly all of it spent waiting.
Cases are independent by construction. Each one opens its own conversation and its own DI scope, and the mock seam, the model override and the tool recorder all find their case by conversation id in a ConcurrentDictionary, never by an ambient "current case" -- so two cases in flight cannot see each other. The executor now runs up to MaxParallelCases work items at a time, taken from the run's own value, else the suite's (default 4, 1 to 16, rejected rather than clamped at the API).
What must not overlap is this layer's own bookkeeping: the run document's counts are a read-modify-write of one document, and two of those interleaved lose whichever increment landed first. Each case's tally is therefore taken under a semaphore while only the case executions themselves overlap, which costs one round trip against a case's tens of seconds.
Cancelling now distinguishes "stop starting new work" from "the run ended early because of it". A cancel that lands while the last case is executing holds nothing back, so that run still ends in its real status rather than being retroactively Cancelled. Cases already in flight cannot be aborted -- BotSharp's SendMessage takes no CancellationToken -- so they finish and record instead of losing work already paid for.
The run records the concurrency it ran at, because a latency percentile from a four-at-a-time run is not the same measurement as one from a serial run, and the executor logs one line per finished case: interleaved cases make progress unreadable from a case's own log lines.