@@ -317,6 +317,92 @@ describe("qa suite runtime launcher", () => {
317317 expect ( runQaTestFileScenarios ) . toHaveBeenCalledTimes ( 1 ) ;
318318 } ) ;
319319
320+ it ( "serializes channel-driver isolated flow workers under explicit concurrency" , async ( ) => {
321+ const repoRoot = await makeTempRepo ( "qa-suite-crabline-isolated-" ) ;
322+ const defaultFlowImplementation = runQaFlowSuite . getMockImplementation ( ) ;
323+ if ( ! defaultFlowImplementation ) {
324+ throw new Error ( "expected default QA flow suite mock implementation" ) ;
325+ }
326+ const isolatedScenarioIds = new Set ( [
327+ "runtime-tool-image-generate" ,
328+ "runtime-inventory-drift-check" ,
329+ "session-memory-ranking" ,
330+ ] ) ;
331+ let activeIsolatedWorkers = 0 ;
332+ let maxActiveIsolatedWorkers = 0 ;
333+ runQaFlowSuite . mockImplementation (
334+ async (
335+ params :
336+ | { outputDir ?: string ; scenarioIds ?: string [ ] ; writeEvidenceFile ?: boolean }
337+ | undefined ,
338+ ) => {
339+ const scenarioIds = params ?. scenarioIds ?? [ ] ;
340+ const isolatedWorker = scenarioIds . some ( ( scenarioId ) =>
341+ isolatedScenarioIds . has ( scenarioId ) ,
342+ ) ;
343+ if ( ! isolatedWorker ) {
344+ return await defaultFlowImplementation ( params ) ;
345+ }
346+ activeIsolatedWorkers += 1 ;
347+ maxActiveIsolatedWorkers = Math . max ( maxActiveIsolatedWorkers , activeIsolatedWorkers ) ;
348+ await new Promise < void > ( ( resolve ) => {
349+ setTimeout ( resolve , 1 ) ;
350+ } ) ;
351+ try {
352+ return await defaultFlowImplementation ( params ) ;
353+ } finally {
354+ activeIsolatedWorkers -= 1 ;
355+ }
356+ } ,
357+ ) ;
358+
359+ await runQaSuite ( {
360+ repoRoot,
361+ outputDir : ".artifacts/qa-e2e/crabline-isolated" ,
362+ channelDriverSelection : {
363+ capabilityMatrixPath : "crabline-fake-provider-capabilities.json" ,
364+ channel : "telegram" ,
365+ channelDriver : "crabline" ,
366+ smokeArtifactPath : "crabline-fake-provider-smoke.json" ,
367+ } ,
368+ concurrency : 8 ,
369+ scenarioIds : [
370+ "channel-chat-baseline" ,
371+ "runtime-tool-image-generate" ,
372+ "runtime-inventory-drift-check" ,
373+ "session-memory-ranking" ,
374+ "control-ui-chat-flow-playwright" ,
375+ ] ,
376+ } ) ;
377+
378+ const outputDir = path . join ( repoRoot , ".artifacts" , "qa-e2e" , "crabline-isolated" ) ;
379+ expect ( runQaFlowSuite ) . toHaveBeenCalledTimes ( 4 ) ;
380+ expect ( runQaFlowSuite ) . toHaveBeenNthCalledWith (
381+ 1 ,
382+ expect . objectContaining ( {
383+ outputDir : path . join ( outputDir , "flow" , "shared" ) ,
384+ concurrency : 1 ,
385+ scenarioIds : [ "channel-chat-baseline" ] ,
386+ } ) ,
387+ ) ;
388+ for ( const [ index , scenarioId ] of [
389+ "runtime-tool-image-generate" ,
390+ "runtime-inventory-drift-check" ,
391+ "session-memory-ranking" ,
392+ ] . entries ( ) ) {
393+ expect ( runQaFlowSuite ) . toHaveBeenNthCalledWith (
394+ index + 2 ,
395+ expect . objectContaining ( {
396+ outputDir : path . join ( outputDir , "flow" , `isolated-${ index + 1 } ` ) ,
397+ concurrency : 1 ,
398+ scenarioIds : [ scenarioId ] ,
399+ } ) ,
400+ ) ;
401+ }
402+ expect ( runQaTestFileScenarios ) . toHaveBeenCalledTimes ( 1 ) ;
403+ expect ( maxActiveIsolatedWorkers ) . toBe ( 1 ) ;
404+ } ) ;
405+
320406 it ( "respects serial concurrency across unified suite partitions" , async ( ) => {
321407 const repoRoot = await makeTempRepo ( "qa-suite-serial-" ) ;
322408 let releaseFlow ! : ( ) => void ;
0 commit comments