@@ -415,60 +415,43 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
415415 staticSiteImporterSlug : options . staticSiteImporterSlug ,
416416 dependencyOverrides,
417417 } ) ;
418- const dependencyPlan = await discoverFixtureDependencyPlan ( { fixtures, outputDirectory, staticSiteImporterPath, options, dependencyOverlays, batchSuffix } ) ;
419- const resolvedDependencyPlan = await resolveHostDependencyPlan ( dependencyPlan , path . join ( outputDirectory , 'dependency-cache' ) ) ;
420- const batchRecipe = buildFixtureMatrixRecipe ( {
421- matrix : batchMatrix ,
422- runId : batchMatrix . id ,
423- attemptId : batchSuffix ,
424- artifactsDirectory : outputDirectory ,
425- playgroundArtifactsDirectory : options . playgroundArtifactsDirectory || '/wordpress/wp-content/uploads/static-site-importer-fixture-matrix' ,
426- wordpressVersion : options . wordpressVersion ,
427- staticSiteImporterPath,
428- staticSiteImporterPlugin : options . staticSiteImporterPlugin ,
429- staticSiteImporterSlug : options . staticSiteImporterSlug ,
430- dependencyPlan : resolvedDependencyPlan ,
431- dependencyOverrides,
432- dependencyOverlays,
433- svgFontEvidence : true ,
434- ...fixtureMatrixRecipeInput ( normalizeFixtureMatrixRunConfig ( Object . fromEntries ( Object . keys ( FIXTURE_MATRIX_RUN_FIELDS ) . map ( ( key ) => [ key , options [ key ] ] ) ) ) ) ,
435- ...visualParityRecipeInput ( options ) ,
436- ...liveWpParityRecipeInput ( options ) ,
437- ...runtimePresentationEvidenceRecipeInput ( options ) ,
438- } ) ;
439418 const batchRecipeFile = path . join ( outputDirectory , `wp-codebox-static-site-fixture-matrix-batch-${ batchSuffix } .json` ) ;
440419 const outputFile = path . join ( outputDirectory , `wp-codebox-output-batch-${ batchSuffix } .json` ) ;
441420 const codeboxArtifactsDirectory = batchCodeboxArtifactsDirectory ( outputDirectory , batchSuffix ) ;
442421 const artifactRefs = batchArtifactRefs ( { outputDirectory, batchSuffix, batchRecipeFile, outputFile, codeboxArtifactsDirectory } ) ;
443- writeJsonArtifact ( batchRecipeFile , batchRecipe ) ;
444- progress ?. emit ( recovery ? 'recovery' : 'batch' , 'started' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery } ) ;
445- progress ?. emit ( 'fixture' , 'started' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery } ) ;
446422
447- let batchRuntime = null ;
423+ let dependencyPlan = null ;
424+ let resolvedDependencyPlan = null ;
425+ let batchRecipe = null ;
448426 let batchError = null ;
449427 let childCommandFailure = null ;
450- let childRecipeRunMs = 0 ;
451- const childRecipeRunStartedAt = nowMs ( ) ;
428+
452429 try {
453- batchRuntime = await runWpCodeboxRecipe ( {
454- recipeFile : batchRecipeFile ,
455- artifactsDir : codeboxArtifactsDirectory ,
456- outputFile,
457- cwd : outputDirectory ,
458- wpCodeboxBin : options . wpCodeboxBin ,
459- inactivityTimeoutMs : batchInactivityTimeoutMs ( options ) ,
460- onInactivity : ( { timeout_ms } ) => {
461- progress ?. emit ( 'batch' , 'timeout' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery, timeout_ms } ) ;
462- if ( recovery ) progress ?. emit ( 'fixture' , 'timeout' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery, timeout_ms } ) ;
463- } ,
430+ dependencyPlan = await discoverFixtureDependencyPlan ( { fixtures, outputDirectory, staticSiteImporterPath, options, dependencyOverlays, batchSuffix } ) ;
431+ resolvedDependencyPlan = await resolveHostDependencyPlan ( dependencyPlan , path . join ( outputDirectory , 'dependency-cache' ) ) ;
432+ batchRecipe = buildFixtureMatrixRecipe ( {
433+ matrix : batchMatrix ,
434+ runId : batchMatrix . id ,
435+ attemptId : batchSuffix ,
436+ artifactsDirectory : outputDirectory ,
437+ playgroundArtifactsDirectory : options . playgroundArtifactsDirectory || '/wordpress/wp-content/uploads/static-site-importer-fixture-matrix' ,
438+ wordpressVersion : options . wordpressVersion ,
439+ staticSiteImporterPath,
440+ staticSiteImporterPlugin : options . staticSiteImporterPlugin ,
441+ staticSiteImporterSlug : options . staticSiteImporterSlug ,
442+ dependencyPlan : resolvedDependencyPlan ,
443+ dependencyOverrides,
444+ dependencyOverlays,
445+ svgFontEvidence : true ,
446+ ...fixtureMatrixRecipeInput ( normalizeFixtureMatrixRunConfig ( Object . fromEntries ( Object . keys ( FIXTURE_MATRIX_RUN_FIELDS ) . map ( ( key ) => [ key , options [ key ] ] ) ) ) ) ,
447+ ...visualParityRecipeInput ( options ) ,
448+ ...liveWpParityRecipeInput ( options ) ,
449+ ...runtimePresentationEvidenceRecipeInput ( options ) ,
464450 } ) ;
451+ writeJsonArtifact ( batchRecipeFile , batchRecipe ) ;
465452 } catch ( error ) {
453+ const failureStage = ! dependencyPlan ? 'dependency_discovery' : ! resolvedDependencyPlan ? 'dependency_resolution' : 'recipe_build' ;
466454 batchError = error ;
467- batchRuntime = {
468- exitCode : error ?. code ?? 1 ,
469- outputFile,
470- json : parseJsonText ( error ?. stdout ) ,
471- } ;
472455 childCommandFailure = buildWpCodeboxChildCommandFailure ( {
473456 error,
474457 fixtures,
@@ -480,9 +463,54 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
480463 artifactsDir : codeboxArtifactsDirectory ,
481464 wpCodeboxBin : options . wpCodeboxBin ,
482465 artifactRefs,
466+ failureStage,
483467 } ) ;
484- } finally {
485- childRecipeRunMs = elapsedMs ( childRecipeRunStartedAt ) ;
468+ }
469+
470+ progress ?. emit ( recovery ? 'recovery' : 'batch' , batchError ? 'failed' : 'started' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery, ...( batchError ? { failure_stage : childCommandFailure ?. failure_stage } : { } ) } ) ;
471+ if ( ! batchError ) {
472+ progress ?. emit ( 'fixture' , 'started' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery } ) ;
473+ }
474+
475+ let batchRuntime = null ;
476+ let childRecipeRunMs = 0 ;
477+ const childRecipeRunStartedAt = nowMs ( ) ;
478+ if ( ! batchError ) {
479+ try {
480+ batchRuntime = await runWpCodeboxRecipe ( {
481+ recipeFile : batchRecipeFile ,
482+ artifactsDir : codeboxArtifactsDirectory ,
483+ outputFile,
484+ cwd : outputDirectory ,
485+ wpCodeboxBin : options . wpCodeboxBin ,
486+ inactivityTimeoutMs : batchInactivityTimeoutMs ( options ) ,
487+ onInactivity : ( { timeout_ms } ) => {
488+ progress ?. emit ( 'batch' , 'timeout' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery, timeout_ms } ) ;
489+ if ( recovery ) progress ?. emit ( 'fixture' , 'timeout' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery, timeout_ms } ) ;
490+ } ,
491+ } ) ;
492+ } catch ( error ) {
493+ batchError = error ;
494+ batchRuntime = {
495+ exitCode : error ?. code ?? 1 ,
496+ outputFile,
497+ json : parseJsonText ( error ?. stdout ) ,
498+ } ;
499+ childCommandFailure = buildWpCodeboxChildCommandFailure ( {
500+ error,
501+ fixtures,
502+ batchNumber,
503+ batchSuffix,
504+ batchId : `batch-${ String ( batchNumber ) . padStart ( 3 , '0' ) } ` ,
505+ batchRecipeFile,
506+ outputFile,
507+ artifactsDir : codeboxArtifactsDirectory ,
508+ wpCodeboxBin : options . wpCodeboxBin ,
509+ artifactRefs,
510+ } ) ;
511+ } finally {
512+ childRecipeRunMs = elapsedMs ( childRecipeRunStartedAt ) ;
513+ }
486514 }
487515
488516 const batchRun = fixtureMatrixBatchRunSummary ( {
@@ -494,6 +522,7 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
494522 codeboxArtifactsDirectory,
495523 batchRuntime,
496524 batchError,
525+ exitCode : childCommandFailure ?. exit_status ,
497526 performance : {
498527 child_recipe_run_ms : childRecipeRunMs ,
499528 } ,
@@ -513,7 +542,7 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
513542 visualParity : fixtureMatrixGateConfig ( normalizeFixtureMatrixRunConfig ( Object . fromEntries ( Object . keys ( FIXTURE_MATRIX_RUN_FIELDS ) . map ( ( key ) => [ key , options [ key ] ] ) ) ) ) . visualParity ,
514543 liveWpParity : liveWpParityCollectorInput ( options ) ,
515544 dependencyOverrides,
516- dependencyOverlays : batchRecipe . inputs . dependency_overlays || [ ] ,
545+ dependencyOverlays : batchRecipe ? .inputs ? .dependency_overlays || [ ] ,
517546 } ) ;
518547 const visualCompare = materializeVisualCompareArtifacts ( {
519548 result : batchResult ,
@@ -547,6 +576,20 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
547576 } ;
548577 }
549578
579+ // Dependency discovery, host resolution, and recipe build failures are
580+ // planning failures, not sandbox corruption. Re-running each fixture
581+ // individually would fail identically — return the typed failure directly.
582+ if ( childCommandFailure ?. failure_stage && childCommandFailure . failure_stage !== 'recipe_run' ) {
583+ return {
584+ batchRun,
585+ batchResult : editorCanvas . result ,
586+ visualParityArtifacts : visualCompare . artifacts ,
587+ editorCanvasArtifacts : editorCanvas . artifacts ,
588+ error : batchError ,
589+ childCommandFailures : [ childCommandFailure ] ,
590+ } ;
591+ }
592+
550593 // A recipe-level failure leaves the sandbox's state untrustworthy. Re-run each
551594 // fixture in a fresh sandbox so one stalled step cannot classify its batch peers.
552595 progress ?. emit ( 'recovery' , 'started' , { fixture_id : fixtures [ 0 ] ?. id || '' , batch : batchNumber , recovery : true } ) ;
@@ -1411,7 +1454,7 @@ export function fixtureMatrixBatchRunSummary(input = {}) {
14111454 recipe_file : input . batchRecipeFile || '' ,
14121455 output_file : input . outputFile || '' ,
14131456 codebox_artifacts_directory : input . codeboxArtifactsDirectory || '' ,
1414- exit_code : batchRuntime ?. exitCode ?? 0 ,
1457+ exit_code : batchRuntime ?. exitCode ?? input . exitCode ?? 0 ,
14151458 error : batchError ? batchError . message : '' ,
14161459 stderr_tail : batchError ? textTail ( batchError . stderr ) : '' ,
14171460 stdout_tail : batchError ? textTail ( batchError . stdout ) : '' ,
@@ -1447,17 +1490,18 @@ function runtimeSummary(runtime, runtimeError) {
14471490 } ;
14481491}
14491492
1450- function buildWpCodeboxChildCommandFailure ( { error, fixtures, batchNumber, batchSuffix, batchId, batchRecipeFile, outputFile, artifactsDir, wpCodeboxBin : bin , artifactRefs } ) {
1451- const command = wpCodeboxRecipeRunCommand ( { recipeFile : batchRecipeFile , artifactsDir, outputFile, wpCodeboxBin : bin } ) ;
1493+ function buildWpCodeboxChildCommandFailure ( { error, fixtures, batchNumber, batchSuffix, batchId, batchRecipeFile, outputFile, artifactsDir, wpCodeboxBin : bin , artifactRefs, failureStage = 'recipe_run' } ) {
1494+ const isRecipeRun = failureStage === 'recipe_run' ;
1495+ const command = isRecipeRun ? wpCodeboxRecipeRunCommand ( { recipeFile : batchRecipeFile , artifactsDir, outputFile, wpCodeboxBin : bin } ) : null ;
14521496 return {
14531497 schema : 'homeboy/child-command-failure/v1' ,
14541498 kind : 'child_command_failed' ,
1455- label : `WP Codebox recipe-run batch ${ batchSuffix } ` ,
1499+ label : isRecipeRun ? `WP Codebox recipe-run batch ${ batchSuffix } ` : `Dependency ${ failureStage } batch ${ batchSuffix } `,
14561500 batch : batchNumber ,
14571501 batch_id : batchId || `batch-${ batchSuffix } ` ,
14581502 fixture_ids : normalizeFixtureIds ( fixtures ) ,
1459- command : command . command ,
1460- command_argv : command . argv ,
1503+ ... ( command ? { command : command . command } : { } ) ,
1504+ ... ( command ? { command_argv : command . argv } : { } ) ,
14611505 exit_status : exitStatus ( error ) ,
14621506 error_code : error ?. code ,
14631507 error_signal : error ?. signal ,
@@ -1466,9 +1510,10 @@ function buildWpCodeboxChildCommandFailure({ error, fixtures, batchNumber, batch
14661510 recipe_file : batchRecipeFile ,
14671511 output_file : outputFile ,
14681512 artifacts_directory : artifactsDir ,
1469- replay_command : wpCodeboxReplayCommand ( { recipeFile : batchRecipeFile , artifactsDir, wpCodeboxBin : bin } ) ,
1513+ ... ( isRecipeRun ? { replay_command : wpCodeboxReplayCommand ( { recipeFile : batchRecipeFile , artifactsDir, wpCodeboxBin : bin } ) } : { } ) ,
14701514 artifact_refs : artifactRefs ,
1471- message : error ?. message || 'WP Codebox recipe-run failed' ,
1515+ failure_stage : failureStage ,
1516+ message : error ?. message || ( isRecipeRun ? 'WP Codebox recipe-run failed' : `Dependency ${ failureStage } failed` ) ,
14721517 } ;
14731518}
14741519
0 commit comments