@@ -6,8 +6,10 @@ import os from "node:os";
66import path from "node:path" ;
77import process from "node:process" ;
88import {
9+ buildGauntletPrebuildEnv ,
910 collectGatewayCpuObservations ,
1011 collectMetricObservations ,
12+ collectQaBaselineRegressionObservations ,
1113 discoverBundledPluginManifests ,
1214 selectPluginEntries ,
1315} from "./lib/plugin-gateway-gauntlet.mjs" ;
@@ -39,6 +41,7 @@ function parseArgs(argv) {
3941 skipPrebuild : false ,
4042 skipLifecycle : false ,
4143 skipQa : false ,
44+ qaBaseline : false ,
4245 skipSlashHelp : false ,
4346 qaScenarios : [ ] ,
4447 qaPluginChunkSize : DEFAULT_QA_PLUGIN_CHUNK_SIZE ,
@@ -47,6 +50,8 @@ function parseArgs(argv) {
4750 maxRssWarnMb : DEFAULT_MAX_RSS_WARN_MB ,
4851 wallAnomalyMultiplier : 3 ,
4952 rssAnomalyMultiplier : 2.5 ,
53+ qaCpuRegressionMultiplier : 2 ,
54+ qaWallRegressionMultiplier : 2 ,
5055 commandTimeoutMs : 120_000 ,
5156 buildTimeoutMs : 600_000 ,
5257 qaTimeoutMs : 900_000 ,
@@ -90,6 +95,9 @@ function parseArgs(argv) {
9095 case "--qa-plugin-chunk-size" :
9196 options . qaPluginChunkSize = parsePositiveInt ( readValue ( ) , "--qa-plugin-chunk-size" ) ;
9297 break ;
98+ case "--qa-baseline" :
99+ options . qaBaseline = true ;
100+ break ;
93101 case "--cpu-core-warn" :
94102 options . cpuCoreWarn = parsePositiveNumber ( readValue ( ) , "--cpu-core-warn" ) ;
95103 break ;
@@ -108,6 +116,18 @@ function parseArgs(argv) {
108116 case "--rss-anomaly-multiplier" :
109117 options . rssAnomalyMultiplier = parsePositiveNumber ( readValue ( ) , "--rss-anomaly-multiplier" ) ;
110118 break ;
119+ case "--qa-cpu-regression-multiplier" :
120+ options . qaCpuRegressionMultiplier = parsePositiveNumber (
121+ readValue ( ) ,
122+ "--qa-cpu-regression-multiplier" ,
123+ ) ;
124+ break ;
125+ case "--qa-wall-regression-multiplier" :
126+ options . qaWallRegressionMultiplier = parsePositiveNumber (
127+ readValue ( ) ,
128+ "--qa-wall-regression-multiplier" ,
129+ ) ;
130+ break ;
111131 case "--command-timeout-ms" :
112132 options . commandTimeoutMs = parsePositiveInt ( readValue ( ) , "--command-timeout-ms" ) ;
113133 break ;
@@ -156,6 +176,7 @@ Options:
156176 --output-dir <path> Artifact directory
157177 --qa-scenario <id> QA Lab scenario id, repeatable
158178 --qa-plugin-chunk-size <count> Plugins enabled per QA run (default: 12)
179+ --qa-baseline Run a no-extra-plugin QA baseline before plugin chunks
159180 --cpu-core-warn <ratio> Hot CPU threshold (default: 0.9)
160181 --hot-wall-warn-ms <ms> Minimum wall time for hot CPU observations (default: 30000)
161182 --max-rss-warn-mb <mb> Maximum RSS warning threshold (default: 1536)
@@ -416,19 +437,22 @@ function runSlashHelpProbes(params) {
416437}
417438
418439function runQaChunks ( params ) {
419- const chunks = chunkArray ( params . plugins , params . qaPluginChunkSize ) ;
440+ const chunks = [
441+ ...( params . qaBaseline ? [ { label : "baseline" , plugins : [ ] } ] : [ ] ) ,
442+ ...chunkArray ( params . plugins , params . qaPluginChunkSize ) . map ( ( plugins , index ) => ( {
443+ label : `chunk-${ String ( index ) . padStart ( 2 , "0" ) } ` ,
444+ plugins,
445+ } ) ) ,
446+ ] ;
420447 const summaries = [ ] ;
421448 for ( let index = 0 ; index < chunks . length ; index += 1 ) {
422449 const chunk = chunks [ index ] ;
423- const outputDir = path . join (
424- params . outputDir ,
425- "qa-suite" ,
426- `chunk-${ String ( index ) . padStart ( 2 , "0" ) } ` ,
427- ) ;
450+ const outputDir = path . join ( params . outputDir , "qa-suite" , chunk . label ) ;
428451 const outputArg = toRepoRelativePath ( params . repoRoot , outputDir ) ;
429- const pluginIds = chunk . map ( ( plugin ) => plugin . id ) ;
452+ const pluginIds = chunk . plugins . map ( ( plugin ) => plugin . id ) ;
453+ const pluginIdLabel = pluginIds . length > 0 ? pluginIds . join ( "," ) : "<baseline>" ;
430454 process . stderr . write (
431- `[plugin-gauntlet] qa chunk ${ index + 1 } /${ chunks . length } : ${ pluginIds . join ( "," ) } \n` ,
455+ `[plugin-gauntlet] qa chunk ${ index + 1 } /${ chunks . length } : ${ pluginIdLabel } \n` ,
432456 ) ;
433457 const row = runMeasuredCommand ( {
434458 cwd : params . repoRoot ,
@@ -446,14 +470,21 @@ function runQaChunks(params) {
446470 ...params . qaScenarios . flatMap ( ( scenario ) => [ "--scenario" , scenario ] ) ,
447471 ...pluginIds . flatMap ( ( pluginId ) => [ "--enable-plugin" , pluginId ] ) ,
448472 ] ) ,
449- label : `qa-chunk- ${ String ( index ) . padStart ( 2 , "0" ) } ` ,
473+ label : `qa-${ chunk . label } ` ,
450474 phase : "qa:rpc" ,
451475 timeoutMs : params . qaTimeoutMs ,
452476 } ) ;
453- params . rows . push ( { ...row , pluginId : pluginIds . join ( "," ) } ) ;
454477 const summaryPath = path . join ( outputDir , "qa-suite-summary.json" ) ;
478+ const qaSummary = fs . existsSync ( summaryPath )
479+ ? JSON . parse ( fs . readFileSync ( summaryPath , "utf8" ) )
480+ : null ;
481+ params . rows . push ( {
482+ ...row ,
483+ pluginId : pluginIdLabel ,
484+ ...( qaSummary ?. metrics ? { qaMetrics : qaSummary . metrics } : { } ) ,
485+ } ) ;
455486 if ( fs . existsSync ( summaryPath ) ) {
456- summaries . push ( JSON . parse ( fs . readFileSync ( summaryPath , "utf8" ) ) ) ;
487+ summaries . push ( qaSummary ) ;
457488 }
458489 }
459490 return summaries ;
@@ -478,7 +509,7 @@ async function main() {
478509 rows . push (
479510 runMeasuredCommand ( {
480511 cwd : repoRoot ,
481- env,
512+ env : buildGauntletPrebuildEnv ( env , { includePrivateQa : ! options . skipQa } ) ,
482513 logDir : path . join ( options . outputDir , "logs" , "prebuild" ) ,
483514 command : pnpmCommand ( ) ,
484515 args : [ "build" ] ,
@@ -519,6 +550,7 @@ async function main() {
519550 outputDir : options . outputDir ,
520551 env,
521552 plugins : selectedPlugins ,
553+ qaBaseline : options . qaBaseline ,
522554 rows,
523555 qaScenarios : options . qaScenarios ,
524556 qaPluginChunkSize : options . qaPluginChunkSize ,
@@ -531,6 +563,10 @@ async function main() {
531563 wallAnomalyMultiplier : options . wallAnomalyMultiplier ,
532564 rssAnomalyMultiplier : options . rssAnomalyMultiplier ,
533565 } ) ;
566+ const qaBaselineObservations = collectQaBaselineRegressionObservations ( rows , {
567+ cpuRegressionMultiplier : options . qaCpuRegressionMultiplier ,
568+ wallRegressionMultiplier : options . qaWallRegressionMultiplier ,
569+ } ) ;
534570 const gatewayObservations = qaSummaries . flatMap ( ( qa ) =>
535571 collectGatewayCpuObservations ( {
536572 startup : null ,
@@ -554,6 +590,7 @@ async function main() {
554590 limit : options . limit ?? null ,
555591 qaScenarios : options . qaScenarios ,
556592 qaPluginChunkSize : options . qaPluginChunkSize ,
593+ qaBaseline : options . qaBaseline ,
557594 skipLifecycle : options . skipLifecycle ,
558595 skipQa : options . skipQa ,
559596 skipSlashHelp : options . skipSlashHelp ,
@@ -564,12 +601,14 @@ async function main() {
564601 maxRssWarnMb : options . maxRssWarnMb ,
565602 wallAnomalyMultiplier : options . wallAnomalyMultiplier ,
566603 rssAnomalyMultiplier : options . rssAnomalyMultiplier ,
604+ qaCpuRegressionMultiplier : options . qaCpuRegressionMultiplier ,
605+ qaWallRegressionMultiplier : options . qaWallRegressionMultiplier ,
567606 } ,
568607 } ,
569608 matrix,
570609 selectedPlugins,
571610 rows,
572- observations : [ ...metricObservations , ...gatewayObservations ] ,
611+ observations : [ ...metricObservations , ...qaBaselineObservations , ... gatewayObservations ] ,
573612 failures,
574613 } ;
575614 const summaryPath = path . join ( options . outputDir , "plugin-gateway-gauntlet-summary.json" ) ;
0 commit comments