11import { createHash } from "node:crypto"
2+ import { benchRunCode } from "./bench-command-handlers.js"
23
34import {
45 WORDPRESS_HOTSPOTS_SCHEMA ,
@@ -314,7 +315,7 @@ async function executeRollbackSafeRestMutation(
314315export function createWordPressFuzzSuiteRuntimeWorkloadExecutor ( episode : Pick < RuntimeEpisode , "step" > ) : FuzzSuiteRuntimeWorkloadExecutor {
315316 return {
316317 async executeRuntimeWorkload ( { workload, case : fuzzCase } ) {
317- const steps = [ ...workloadSteps ( workload . before , workload ) , ...workloadSteps ( workload . steps , workload ) , ...workloadSteps ( workload . after , workload ) ]
318+ const steps = [ ...workloadSteps ( workload . before , workload , fuzzCase ) , ...workloadSteps ( workload . steps , workload , fuzzCase ) , ...workloadSteps ( workload . after , workload , fuzzCase ) ]
318319 const startedAt = new Date ( ) . toISOString ( )
319320 const executions : ExecutionResult [ ] = [ ]
320321 for ( const step of steps ) {
@@ -424,17 +425,17 @@ export function executeWordPressFuzzSuite(
424425 } ,
425426 runtimeActionExecutor : async ( input ) => {
426427 const observation = await runtimeActionExecutor . executeRuntimeAction ( input )
427- pushHotspotObservation ( hotspotObservations , performanceObservationFromRuntimeAction ( observation ) , runtimeActionArtifactRefs ( observation ) )
428+ pushHotspotObservation ( hotspotObservations , performanceObservationFromRuntimeAction ( observation ) , runtimeActionArtifactRefs ( observation ) , { caseId : input . case . id , targetId : input . target . id ?? input . target . entrypoint , phase : input . action . type } )
428429 return observation
429430 } ,
430431 runtimeWorkloadExecutor : async ( input ) => {
431432 const execution = await runtimeWorkloadExecutor . executeRuntimeWorkload ( input )
432433 const observations = performanceObservationsFromWorkloadExecution ( execution )
433434 if ( observations . length === 0 ) {
434- pushHotspotObservation ( hotspotObservations , performanceObservationFromExecution ( { command : execution . command , args : execution . args } , execution ) , executionArtifactRefs ( execution ) )
435+ pushHotspotObservation ( hotspotObservations , performanceObservationFromExecution ( { command : execution . command , args : execution . args } , execution ) , executionArtifactRefs ( execution ) , { caseId : input . case . id , targetId : input . target . id ?? input . target . entrypoint , phase : input . target . entrypoint ?? input . target . kind } )
435436 } else {
436437 for ( const observation of observations ) {
437- pushHotspotObservation ( hotspotObservations , observation , executionArtifactRefs ( execution ) )
438+ pushHotspotObservation ( hotspotObservations , observation , executionArtifactRefs ( execution ) , { caseId : input . case . id , targetId : input . target . id ?? input . target . entrypoint , phase : input . target . entrypoint ?? input . target . kind } )
438439 }
439440 }
440441 return execution
@@ -458,7 +459,11 @@ function resultWithWordPressHotspotsArtifact(result: FuzzSuiteResultEnvelope, ob
458459 artifactRefs : result . artifactRefs ,
459460 metadata : { suiteId : result . suite . id , runnerMode : result . metadata ?. runnerMode } ,
460461 } )
462+ const homeboyObservationSet = homeboyFuzzObservationSetArtifact ( result , observations )
463+ const homeboyHotspotSet = homeboyFuzzHotspotSetArtifact ( result , homeboyObservationSet . observations )
461464 const content = `${ JSON . stringify ( artifact , null , 2 ) } \n`
465+ const homeboyObservationContent = `${ JSON . stringify ( homeboyObservationSet , null , 2 ) } \n`
466+ const homeboyHotspotContent = `${ JSON . stringify ( homeboyHotspotSet , null , 2 ) } \n`
462467 const ref : FuzzSuiteArtifactRef = {
463468 path : "files/wordpress-hotspots.json" ,
464469 kind : "wordpress-hotspots" ,
@@ -468,28 +473,151 @@ function resultWithWordPressHotspotsArtifact(result: FuzzSuiteResultEnvelope, ob
468473 name : "wordpress-hotspots" ,
469474 metadata : { schema : WORDPRESS_HOTSPOTS_SCHEMA , source : "executeWordPressFuzzSuite" } ,
470475 }
476+ const homeboyObservationRef : FuzzSuiteArtifactRef = homeboyArtifactRef ( "files/fuzz-observations.json" , "fuzz-observation-set" , "homeboy/fuzz-observation-set/v1" , homeboyObservationContent )
477+ const homeboyHotspotRef : FuzzSuiteArtifactRef = homeboyArtifactRef ( "files/fuzz-hotspots.json" , "fuzz-hotspot-set" , "homeboy/fuzz-hotspot-set/v1" , homeboyHotspotContent )
471478
472479 return {
473480 ...result ,
474- artifactRefs : dedupeFuzzSuiteArtifactRefs ( [ ...result . artifactRefs , ref ] ) ,
481+ artifactRefs : dedupeFuzzSuiteArtifactRefs ( [ ...result . artifactRefs , ref , homeboyObservationRef , homeboyHotspotRef ] ) ,
475482 metadata : {
476483 ...result . metadata ,
477484 artifacts : {
478485 ...( recordValue ( result . metadata ?. artifacts ) ?? { } ) ,
479486 wordpressHotspots : artifact ,
487+ fuzzObservationSet : homeboyObservationSet ,
488+ fuzzHotspotSet : homeboyHotspotSet ,
480489 } ,
481490 } ,
482491 }
483492}
484493
485- function pushHotspotObservation ( out : WordPressHotspotObservationInput [ ] , observation : PerformanceObservation | undefined , artifactRefs : FuzzSuiteArtifactRef [ ] = [ ] ) : void {
494+ function pushHotspotObservation ( out : WordPressHotspotObservationInput [ ] , observation : PerformanceObservation | undefined , artifactRefs : FuzzSuiteArtifactRef [ ] = [ ] , metadata : Record < string , unknown > = { } ) : void {
486495 if ( ! observation ) return
487496 out . push ( {
488497 observation,
489498 artifactRefs : artifactRefs . map ( ( ref ) => ( { path : ref . path , kind : ref . kind , contentType : ref . contentType , sha256 : ref . sha256 , bytes : ref . bytes , name : ref . name , metadata : ref . metadata } ) ) ,
499+ metadata,
490500 } )
491501}
492502
503+ function homeboyArtifactRef ( path : string , kind : string , schema : string , content : string ) : FuzzSuiteArtifactRef {
504+ return {
505+ path,
506+ kind,
507+ contentType : "application/json" ,
508+ sha256 : createHash ( "sha256" ) . update ( content ) . digest ( "hex" ) ,
509+ bytes : Buffer . byteLength ( content ) ,
510+ name : kind ,
511+ metadata : { schema, source : "executeWordPressFuzzSuite" } ,
512+ }
513+ }
514+
515+ function homeboyFuzzObservationSetArtifact ( result : FuzzSuiteResultEnvelope , observations : WordPressHotspotObservationInput [ ] ) : { schema : string ; generated_at : string ; source : string ; observations : Array < Record < string , unknown > > ; summary : Record < string , unknown > ; metadata : Record < string , unknown > } {
516+ const flattened = observations . flatMap ( ( input ) => homeboyFuzzObservations ( input ) )
517+ return {
518+ schema : "homeboy/fuzz-observation-set/v1" ,
519+ generated_at : new Date ( ) . toISOString ( ) ,
520+ source : "wp-codebox" ,
521+ observations : flattened ,
522+ summary : {
523+ total : flattened . length ,
524+ families : countByString ( flattened , "family" ) ,
525+ metrics : countByString ( flattened , "metric" ) ,
526+ } ,
527+ metadata : { suite_id : result . suite . id , runner : "wp-codebox" , runner_mode : result . metadata ?. runnerMode } ,
528+ }
529+ }
530+
531+ function homeboyFuzzHotspotSetArtifact ( result : FuzzSuiteResultEnvelope , observations : Array < Record < string , unknown > > ) : { schema : string ; generated_at : string ; source : string ; hotspots : Array < Record < string , unknown > > ; summary : Record < string , unknown > ; metadata : Record < string , unknown > } {
532+ const grouped = new Map < string , Record < string , unknown > [ ] > ( )
533+ for ( const observation of observations ) {
534+ const key = [ stringValue ( observation . case_id ) , stringValue ( observation . target_id ) , stringValue ( observation . subject ) , stringValue ( observation . metric ) ] . join ( "|" )
535+ grouped . set ( key , [ ...( grouped . get ( key ) ?? [ ] ) , observation ] )
536+ }
537+ const scored = [ ...grouped . values ( ) ] . map ( ( items ) => {
538+ const first = items [ 0 ] ?? { }
539+ const value = items . reduce ( ( sum , item ) => sum + ( numberValue ( item . value ) ?? 0 ) , 0 )
540+ return {
541+ family : first . family ,
542+ case_id : first . case_id ,
543+ target_id : first . target_id ,
544+ operation_id : first . operation_id ,
545+ phase : first . phase ,
546+ subject : first . subject ,
547+ metric : first . metric ,
548+ value,
549+ unit : first . unit ,
550+ score : homeboyHotspotScore ( stringValue ( first . metric ) , value ) ,
551+ sample_count : items . reduce ( ( sum , item ) => sum + ( numberValue ( item . sample_count ) ?? 1 ) , 0 ) ,
552+ metadata : { sources : items . length } ,
553+ }
554+ } ) . sort ( ( a , b ) => b . score - a . score || String ( a . subject ) . localeCompare ( String ( b . subject ) ) )
555+ const maxScore = scored [ 0 ] ?. score ?? 0
556+ const hotspots = scored . map ( ( item , index ) => ( { ...item , rank : index + 1 , relative_score : maxScore > 0 ? Number ( ( item . score / maxScore ) . toFixed ( 6 ) ) : 0 } ) )
557+ return {
558+ schema : "homeboy/fuzz-hotspot-set/v1" ,
559+ generated_at : new Date ( ) . toISOString ( ) ,
560+ source : "wp-codebox" ,
561+ hotspots,
562+ summary : { total : hotspots . length , families : countByString ( hotspots , "family" ) , max_score : maxScore } ,
563+ metadata : { suite_id : result . suite . id , runner : "wp-codebox" , runner_mode : result . metadata ?. runnerMode } ,
564+ }
565+ }
566+
567+ function homeboyFuzzObservations ( input : WordPressHotspotObservationInput ) : Array < Record < string , unknown > > {
568+ const observation = input . observation
569+ const metadata = recordValue ( input . metadata ) ?? { }
570+ const common = {
571+ case_id : stringValue ( metadata . caseId ) ?? stringValue ( observation . metadata ?. caseId ) ,
572+ target_id : stringValue ( metadata . targetId ) ?? observation . target ,
573+ operation_id : observation . command ,
574+ phase : stringValue ( metadata . phase ) ?? observation . kind ,
575+ subject : observation . target ?? observation . command ?? observation . kind ?? "observation" ,
576+ metadata : { source : observation . source , execution_id : stringValue ( observation . metadata ?. executionId ) } ,
577+ }
578+ return [
579+ homeboyFuzzObservation ( common , "timing" , "duration-ms" , observation . timing ?. durationMs , "ms" ) ,
580+ homeboyFuzzObservation ( common , "database" , "query-count" , observation . database ?. queryCount , "count" ) ,
581+ homeboyFuzzObservation ( common , "database" , "query-time-ms" , observation . database ?. totalTimeMs , "ms" ) ,
582+ homeboyFuzzObservation ( common , "memory" , "memory-delta-bytes" , observation . memory ?. deltaBytes , "bytes" ) ,
583+ homeboyFuzzObservation ( common , "network" , "network-failures" , observation . network ?. failures , "count" ) ,
584+ ...Object . entries ( observation . browser ?. metrics ?? { } ) . map ( ( [ name , value ] ) => homeboyFuzzObservation ( { ...common , subject : `${ common . subject } :${ name } ` } , "browser" , name , value , undefined ) ) ,
585+ ] . filter ( ( item ) : item is Record < string , unknown > => Boolean ( item ) )
586+ }
587+
588+ function homeboyFuzzObservation ( common : Record < string , unknown > , family : string , metric : string , value : unknown , unit : string | undefined ) : Record < string , unknown > | undefined {
589+ const number = numberValue ( value )
590+ if ( number === undefined || number <= 0 ) return undefined
591+ return {
592+ family,
593+ case_id : common . case_id ,
594+ target_id : common . target_id ,
595+ operation_id : common . operation_id ,
596+ phase : common . phase ,
597+ subject : common . subject ,
598+ metric,
599+ value : number ,
600+ unit,
601+ sample_count : 1 ,
602+ metadata : common . metadata ,
603+ }
604+ }
605+
606+ function homeboyHotspotScore ( metric : string | undefined , value : number ) : number {
607+ if ( metric === "query-count" ) return value * 10
608+ if ( metric === "network-failures" ) return value * 100
609+ if ( metric === "memory-delta-bytes" ) return value / 1024 / 1024
610+ return value
611+ }
612+
613+ function countByString ( items : Array < Record < string , unknown > > , key : string ) : Record < string , number > {
614+ return items . reduce < Record < string , number > > ( ( summary , item ) => {
615+ const value = stringValue ( item [ key ] )
616+ if ( value ) summary [ value ] = ( summary [ value ] ?? 0 ) + 1
617+ return summary
618+ } , { } )
619+ }
620+
493621function performanceObservationFromRuntimeAction ( observation : RuntimeActionObservation ) : PerformanceObservation | undefined {
494622 if ( observation . performance ) {
495623 const command = observation . performance . command ?? observation . step ?. execution . command
@@ -536,7 +664,40 @@ function performanceObservationFromExecution(spec: Partial<ExecutionSpec>, execu
536664function performanceObservationsFromWorkloadExecution ( execution : ExecutionResult ) : PerformanceObservation [ ] {
537665 const json = recordValue ( execution . result ?. json ) ?? parseJsonRecord ( execution . stdout )
538666 const raw = arrayValue ( json ?. observations ?? json ?. performanceObservations ?? json ?. performance_observations )
539- return raw . flatMap ( ( item ) => isPerformanceObservation ( item ) ? [ item ] : [ ] )
667+ return [
668+ ...raw . flatMap ( ( item ) => isPerformanceObservation ( item ) ? [ item ] : [ ] ) ,
669+ ...performanceObservationsFromBenchResult ( json , execution ) ,
670+ ]
671+ }
672+
673+ function performanceObservationsFromBenchResult ( json : Record < string , unknown > | undefined , execution : ExecutionResult ) : PerformanceObservation [ ] {
674+ if ( json ?. schema !== "wp-codebox/bench-results/v1" ) return [ ]
675+ const observations : PerformanceObservation [ ] = [ ]
676+ for ( const scenario of arrayValue ( json . scenarios ) ) {
677+ const scenarioRecord = recordValue ( scenario )
678+ const profile = recordValue ( recordValue ( scenarioRecord ?. artifacts ) ?. [ "rest-db-query-profile" ] )
679+ if ( profile ?. schema !== "wp-codebox/wordpress-rest-db-query-profile/v1" ) continue
680+ for ( const profileCase of arrayValue ( profile . cases ) ) {
681+ const caseRecord = recordValue ( profileCase )
682+ const summary = recordValue ( caseRecord ?. summary )
683+ const queryCount = numberValue ( summary ?. query_count )
684+ const totalTimeMs = numberValue ( summary ?. total_time_ms )
685+ const path = stringValue ( caseRecord ?. path ) ?? stringValue ( caseRecord ?. case_id ) ?? "rest-db-query-profile"
686+ const observation : PerformanceObservation = {
687+ schema : "wp-codebox/performance-observation/v1" ,
688+ command : "wordpress.run-workload" ,
689+ target : path ,
690+ source : "rest-db-query-profiler" ,
691+ kind : "rest-request" ,
692+ database : queryCount !== undefined || totalTimeMs !== undefined ? { queryCount, totalTimeMs } : undefined ,
693+ metadata : { executionId : execution . id , scenarioId : stringValue ( scenarioRecord ?. id ) , caseId : stringValue ( caseRecord ?. case_id ) } ,
694+ }
695+ if ( hasObservationMetrics ( observation ) ) {
696+ observations . push ( observation )
697+ }
698+ }
699+ }
700+ return observations
540701}
541702
542703function normalizeObservationDatabase ( input : Record < string , unknown > | undefined ) : PerformanceObservation [ "database" ] | undefined {
@@ -673,11 +834,11 @@ function numericRecord(value: Record<string, unknown>): Record<string, number> |
673834 return entries . length > 0 ? Object . fromEntries ( entries ) : undefined
674835}
675836
676- function workloadSteps ( value : unknown , workload : Record < string , unknown > ) : Array < { command : string ; args ?: string [ ] ; timeoutMs ?: number ; allowFailure ?: boolean ; advisory ?: boolean } > {
837+ function workloadSteps ( value : unknown , workload : Record < string , unknown > , fuzzCase ?: unknown ) : Array < { command : string ; args ?: string [ ] ; timeoutMs ?: number ; allowFailure ?: boolean ; advisory ?: boolean } > {
677838 if ( ! Array . isArray ( value ) ) {
678839 return [ ]
679840 }
680- return value . flatMap ( ( step ) => {
841+ const commandSteps = value . flatMap ( ( step ) => {
681842 if ( ! step || typeof step !== "object" || Array . isArray ( step ) ) {
682843 return [ ]
683844 }
@@ -696,6 +857,39 @@ function workloadSteps(value: unknown, workload: Record<string, unknown>): Array
696857 advisory : record . advisory === true ,
697858 } ]
698859 } )
860+ if ( commandSteps . length > 0 ) {
861+ return commandSteps
862+ }
863+ if ( value . some ( ( step ) => step && typeof step === "object" && ! Array . isArray ( step ) && typeof ( step as Record < string , unknown > ) . type === "string" ) ) {
864+ return [ { command : "wordpress.run-php" , args : [ `code=${ typedWorkloadRunnerCode ( workload , fuzzCase ) } ` ] } ]
865+ }
866+ return [ ]
867+ }
868+
869+ function typedWorkloadRunnerCode ( workload : Record < string , unknown > , fuzzCase ?: unknown ) : string {
870+ return benchRunCode ( {
871+ componentId : stringValue ( workload . componentId ) ?? stringValue ( workload . component_id ) ?? "wordpress-workload" ,
872+ pluginSlug : typedWorkloadPluginSlug ( workload , fuzzCase ) ,
873+ iterations : 1 ,
874+ warmupIterations : 0 ,
875+ dependencySlugs : [ ] ,
876+ env : recordValue ( workload . runtime_env ) ?? recordValue ( workload . runtimeEnv ) ?? { } ,
877+ bootstrapFiles : [ ] ,
878+ workloads : [ workload ] ,
879+ lifecycle : { } ,
880+ resetPolicy : { } ,
881+ } )
882+ }
883+
884+ function typedWorkloadPluginSlug ( workload : Record < string , unknown > , fuzzCase ?: unknown ) : string {
885+ const fuzzCaseRecord = recordValue ( fuzzCase )
886+ const explicit = stringValue ( workload . pluginSlug ) ?? stringValue ( workload . plugin_slug ) ?? stringValue ( recordValue ( workload . metadata ) ?. plugin_slug ) ?? stringValue ( recordValue ( fuzzCaseRecord ?. metadata ) ?. plugin_slug )
887+ if ( explicit ) return explicit
888+ const metadata = recordValue ( fuzzCaseRecord ?. metadata )
889+ const caseMetadata = recordValue ( metadata ?. caseMetadata ) ?? recordValue ( metadata ?. case_metadata ) ?? metadata
890+ const activation = stringValue ( recordValue ( recordValue ( recordValue ( caseMetadata ?. intent ) ?. plugin ) ?. activation ) ?. entrypoint ) ?? stringValue ( recordValue ( recordValue ( caseMetadata ?. intent ) ?. plugin ) ?. activation )
891+ const slug = activation ?. split ( "/" ) [ 0 ] ?. trim ( )
892+ return slug || "wp-codebox-workload"
699893}
700894
701895function stepArgMap ( args : string [ ] | undefined ) : Record < string , string > {
0 commit comments