Skip to content

Commit 3a94bf0

Browse files
authored
fix: carry candidate overlays into dependency discovery [AI: OpenAI GPT-5.6 Terra via OpenCode] (#873)
1 parent ce6ba3e commit 3a94bf0

5 files changed

Lines changed: 104 additions & 13 deletions

File tree

bench/static-site-fixture-matrix.bench.mjs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
fixtureMatrixGateConfig,
3131
fixtureMatrixRecipeInput,
3232
fixtureMatrixRunConfigFromEnv,
33+
normalizeFixtureMatrixDependencyOverlays,
3334
normalizeFixtureMatrixRunConfig,
3435
} from '../lib/fixture-matrix.mjs';
3536

@@ -256,6 +257,7 @@ export async function runFixtureMatrix(options) {
256257
outputDirectory,
257258
staticSiteImporterPath,
258259
options,
260+
dependencyOverrides,
259261
progress,
260262
}));
261263
performance.batch_execution_ms = elapsedMs(batchExecutionStartedAt);
@@ -393,7 +395,7 @@ function executionEvidenceMetadata(executionRequested) {
393395
// per-fixture artifact subdirectories, all keyed by the unique batch suffix), so
394396
// many of these can run concurrently without colliding. Returns a stable outcome
395397
// the caller folds back together in batch order.
396-
export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outputDirectory, staticSiteImporterPath, options, recovery = false, progress }) {
398+
export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outputDirectory, staticSiteImporterPath, options, dependencyOverrides = prepareDependencyOverrides(options), recovery = false, progress }) {
397399
const batchNumber = batchIndex + 1;
398400
const batchSuffix = recovery
399401
? `${String(batchNumber).padStart(3, '0')}-recovery-${fixtures[0].id}`
@@ -407,7 +409,13 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
407409
// Discovery is deliberately a separate, short-lived Codebox runtime. It only
408410
// asks SSI for its registry-derived plan; package resolution happens on the
409411
// host while assembling the following fresh import runtime.
410-
const dependencyPlan = await discoverFixtureDependencyPlan({ fixtures, outputDirectory, staticSiteImporterPath, options, batchSuffix });
412+
const dependencyOverlays = normalizeFixtureMatrixDependencyOverlays({
413+
staticSiteImporterPath,
414+
staticSiteImporterPlugin: options.staticSiteImporterPlugin,
415+
staticSiteImporterSlug: options.staticSiteImporterSlug,
416+
dependencyOverrides,
417+
});
418+
const dependencyPlan = await discoverFixtureDependencyPlan({ fixtures, outputDirectory, staticSiteImporterPath, options, dependencyOverlays, batchSuffix });
411419
const resolvedDependencyPlan = await resolveHostDependencyPlan(dependencyPlan, path.join(outputDirectory, 'dependency-cache'));
412420
const batchRecipe = buildFixtureMatrixRecipe({
413421
matrix: batchMatrix,
@@ -420,7 +428,8 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
420428
staticSiteImporterPlugin: options.staticSiteImporterPlugin,
421429
staticSiteImporterSlug: options.staticSiteImporterSlug,
422430
dependencyPlan: resolvedDependencyPlan,
423-
dependencyOverrides: prepareDependencyOverrides(options),
431+
dependencyOverrides,
432+
dependencyOverlays,
424433
svgFontEvidence: true,
425434
...fixtureMatrixRecipeInput(normalizeFixtureMatrixRunConfig(Object.fromEntries(Object.keys(FIXTURE_MATRIX_RUN_FIELDS).map((key) => [key, options[key]])))),
426435
...visualParityRecipeInput(options),
@@ -503,7 +512,8 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
503512
sidecarAttemptId: batchSuffix,
504513
visualParity: fixtureMatrixGateConfig(normalizeFixtureMatrixRunConfig(Object.fromEntries(Object.keys(FIXTURE_MATRIX_RUN_FIELDS).map((key) => [key, options[key]])))).visualParity,
505514
liveWpParity: liveWpParityCollectorInput(options),
506-
dependencyOverrides: prepareDependencyOverrides(options),
515+
dependencyOverrides,
516+
dependencyOverlays: batchRecipe.inputs.dependency_overlays || [],
507517
});
508518
const visualCompare = materializeVisualCompareArtifacts({
509519
result: batchResult,
@@ -547,6 +557,7 @@ export async function runFixtureMatrixBatch({ fixtures, batchIndex, matrix, outp
547557
outputDirectory,
548558
staticSiteImporterPath,
549559
options,
560+
dependencyOverrides,
550561
recovery: true,
551562
progress,
552563
}));
@@ -600,7 +611,7 @@ export async function resolveHostDependencyPlan(plan, cacheDirectory, fetcher =
600611
return { ...plan, entries };
601612
}
602613

603-
async function discoverFixtureDependencyPlan({ fixtures, outputDirectory, staticSiteImporterPath, options, batchSuffix }) {
614+
async function discoverFixtureDependencyPlan({ fixtures, outputDirectory, staticSiteImporterPath, options, dependencyOverlays = [], batchSuffix }) {
604615
const plans = [];
605616
for (const fixture of fixtures) {
606617
const fixtureDirectory = path.join(outputDirectory, fixture.id);
@@ -616,6 +627,7 @@ async function discoverFixtureDependencyPlan({ fixtures, outputDirectory, static
616627
inputs: {
617628
stagedFiles: [{ source: path.join(fixtureDirectory, 'artifact.json'), target: path.join(runtimeDirectory, 'artifact.json') }],
618629
extra_plugins: [{ source: staticSiteImporterPath, slug: options.staticSiteImporterSlug || 'static-site-importer', activate: true }],
630+
...(dependencyOverlays.length ? { dependency_overlays: dependencyOverlays } : {}),
619631
},
620632
workflow: { steps: [
621633
{ command: 'wordpress.wp-cli', args: [`command=plugin activate ${(options.staticSiteImporterPlugin || 'static-site-importer/static-site-importer.php')}`] },
@@ -627,10 +639,10 @@ async function discoverFixtureDependencyPlan({ fixtures, outputDirectory, static
627639
const discovery = await runWpCodeboxRecipe({ recipeFile, artifactsDir, outputFile, wpCodeboxBin: options.wpCodeboxBin, inactivityTimeoutMs: batchInactivityTimeoutMs(options) });
628640
const plan = findDependencyPlan(artifactsDir);
629641
if (!plan) throw new Error(`Dependency discovery did not persist a valid plan for fixture ${fixture.id}.`);
630-
// Discovery only mounts SSI. Provider packages are resolved and activated by
631-
// the final recipe's extra_plugins setup, before its workflow begins; asking
632-
// this runtime for a provider receipt would incorrectly require Jetpack/Woo
633-
// to be installed during planning.
642+
// Discovery only mounts SSI and its declared transformer overlays. Provider
643+
// packages are resolved and activated by the final recipe's extra_plugins
644+
// setup, before its workflow begins; asking this runtime for a provider
645+
// receipt would incorrectly require Jetpack/Woo during planning.
634646
plans.push(plan);
635647
}
636648
const entries = new Map();

lib/fixture-matrix.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export {
4242
export {
4343
buildFixtureArtifact,
4444
buildFixtureMatrixRecipe,
45+
normalizeFixtureMatrixDependencyOverlays,
4546
stageFixtureSource,
4647
wordpressServedPath,
4748
normalizeStaticSiteImporterPlugin,

lib/fixture-matrix/collectors/run-intake.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,10 @@ export function collectMatrixEvidence(payload, options = {}) {
278278
const providerAdapter = objectValue(payload.provider_adapter || payload.providerAdapter || importReport.provider_adapter || importReport.providerAdapter || fixtureDiagnostics.provider_adapter || fixtureDiagnostics.providerAdapter);
279279
const captureContract = objectValue(payload.capture_contract || payload.captureContract || payload.visual_capture || payload.visualCapture || fixtureDiagnostics.capture_contract || fixtureDiagnostics.captureContract);
280280
const sidecar = options.sidecar || { status: 'absent' };
281-
const override = objectValue(options.dependencyOverrides || options.dependency_overrides).blocks_engine_php_transformer || objectValue(options.dependencyOverrides || options.dependency_overrides).blocksEnginePhpTransformer || {};
281+
const declaredOverlays = options.dependencyOverlays || options.dependency_overlays;
282+
const override = Array.isArray(declaredOverlays)
283+
? objectValue(declaredOverlays.find((overlay) => overlay?.kind === 'composer-package' && overlay?.package === 'automattic/blocks-engine-php-transformer' && overlay?.consumer === 'static-site-importer'))
284+
: objectValue(options.dependencyOverrides || options.dependency_overrides).blocks_engine_php_transformer || objectValue(options.dependencyOverrides || options.dependency_overrides).blocksEnginePhpTransformer || {};
282285
const completedMaterialization = objectValue(materializationReceipt.completed);
283286
const generatedTheme = objectValue(importReport.generated_theme || importReport.generatedTheme || payload.generated_theme || payload.generatedTheme);
284287
const templateParts = normalizeArray(generatedTheme.template_parts || generatedTheme.templateParts)

lib/fixture-matrix/steps/recipe-builder.mjs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ export function buildFixtureMatrixRecipe(input = {}) {
212212
const runId = input.runId || input.run_id || `${matrix.id}-${randomUUID()}`;
213213
const attemptId = input.attemptId || input.attempt_id || randomUUID();
214214
const importer = normalizeStaticSiteImporterPlugin(input);
215-
const dependencyOverrideSetup = buildDependencyOverrideSetup(input, importer);
215+
const dependencyOverlays = input.dependencyOverlays || input.dependency_overlays || buildDependencyOverrideSetup(input, importer).dependencyOverlays;
216216
const mounts = normalizeArray(input.mounts);
217217
const stagedFiles = normalizeArray(input.stagedFiles || input.staged_files);
218218
const extraPlugins = [
@@ -279,8 +279,8 @@ export function buildFixtureMatrixRecipe(input = {}) {
279279
mounts,
280280
stagedFiles,
281281
extra_plugins: extraPlugins,
282-
...(dependencyOverrideSetup.dependencyOverlays.length
283-
? { dependency_overlays: dependencyOverrideSetup.dependencyOverlays }
282+
...(dependencyOverlays.length
283+
? { dependency_overlays: dependencyOverlays }
284284
: {}),
285285
},
286286
workflow: {
@@ -326,6 +326,10 @@ export function buildFixtureMatrixRecipe(input = {}) {
326326
};
327327
}
328328

329+
export function normalizeFixtureMatrixDependencyOverlays(input = {}) {
330+
return buildDependencyOverrideSetup(input, normalizeStaticSiteImporterPlugin(input)).dependencyOverlays;
331+
}
332+
329333
function surfaceCoverageRuntimeWarning(surfaceCoverage) {
330334
return {
331335
code: 'surface_coverage_runtime_cost',

tools/fixture-matrix.test.mjs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,6 +1713,26 @@ test('fixture lineage retains the development transformer override identity', ()
17131713
assert.deepEqual(evidence.lineage.development_override, { package: 'automattic/blocks-engine-php-transformer', reference: 'b'.repeat(40) });
17141714
});
17151715

1716+
test('fixture lineage only claims a transformer candidate declared by the effective recipe', () => {
1717+
const reference = 'b'.repeat(40);
1718+
const payload = {
1719+
import_report: {
1720+
blocks_engine: {
1721+
transformer: { package: 'automattic/blocks-engine-php-transformer', version: 'dev-main', reference },
1722+
wordpress_site_plan: { schema: 'blocks-engine/wordpress-site-plan/v2' },
1723+
},
1724+
materialization_receipt: { schema: 'static-site-importer/materialization-receipt/v1', status: 'completed' },
1725+
},
1726+
};
1727+
const dependencyOverrides = { blocks_engine_php_transformer: { package: 'automattic/blocks-engine-php-transformer', reference } };
1728+
1729+
assert.equal(collectMatrixEvidence(payload, { dependencyOverrides, dependencyOverlays: [] }).lineage.development_override, undefined);
1730+
assert.deepEqual(collectMatrixEvidence(payload, {
1731+
dependencyOverrides,
1732+
dependencyOverlays: [{ kind: 'composer-package', package: 'automattic/blocks-engine-php-transformer', consumer: 'static-site-importer', source: '/candidate', reference }],
1733+
}).lineage.development_override, { package: 'automattic/blocks-engine-php-transformer', reference });
1734+
});
1735+
17161736
test('fixture lineage does not correlate a retried provider failure to a transform diagnostic', () => {
17171737
const outputDirectory = mkdtempSync(path.join(tmpdir(), 'ssi-attribution-correlation-'));
17181738
const matrix = createFixtureMatrix({ fixture_root: fixtureRoot, id: 'attribution-correlation-test' });
@@ -4982,6 +5002,12 @@ function wpCodeboxCommand(bin) { return { command: bin, args: [] }; }
49825002
49835003
async function runWpCodeboxRecipe(options = {}) {
49845004
const recipe = fs.readFileSync(options.recipeFile, 'utf8');
5005+
const capturedRecipes = process.env.SSI_TEST_RECIPE_CAPTURE_FILE;
5006+
if (capturedRecipes) {
5007+
const captured = fs.existsSync(capturedRecipes) ? JSON.parse(fs.readFileSync(capturedRecipes, 'utf8')) : [];
5008+
captured.push(JSON.parse(recipe));
5009+
fs.writeFileSync(capturedRecipes, JSON.stringify(captured));
5010+
}
49855011
if (recipe.includes('plan-artifact-dependencies')) {
49865012
fs.mkdirSync(options.artifactsDir, { recursive: true });
49875013
fs.writeFileSync(require('node:path').join(options.artifactsDir, 'dependency-plan.json'), JSON.stringify({ schema: 'static-site-importer/runtime-dependency-plan/v1', artifact_sha256: 'a'.repeat(64), entries: [] }));
@@ -5050,6 +5076,7 @@ const CONCURRENCY_ENV_KEYS = [
50505076
'SSI_TEST_RECIPE_BATCH_COUNT',
50515077
'SSI_TEST_RECIPE_UNIT_MS',
50525078
'SSI_TEST_RECIPE_THROW_BATCH',
5079+
'SSI_TEST_RECIPE_CAPTURE_FILE',
50535080
];
50545081

50555082
function snapshotConcurrencyEnv() {
@@ -5103,6 +5130,50 @@ test('runFixtureMatrix caps WP Codebox batches in flight at the configured concu
51035130
}
51045131
});
51055132

5133+
test('runFixtureMatrix uses the same candidate transformer overlay for dependency discovery and final import', async () => {
5134+
const snapshot = snapshotConcurrencyEnv();
5135+
const workspace = setupConcurrencyWorkspace('ssi-discovery-overlay-', 1);
5136+
const transformerPath = path.join(workspace.root, 'blocks-engine', 'php-transformer');
5137+
const reference = 'c'.repeat(40);
5138+
const captureFile = path.join(workspace.root, 'recipes.json');
5139+
mkdirSync(transformerPath, { recursive: true });
5140+
writeFileSync(path.join(transformerPath, 'composer.json'), JSON.stringify({ name: 'automattic/blocks-engine-php-transformer' }));
5141+
process.env.HOMEBOY_WP_CODEBOX_RECIPE_HELPER = workspace.helperPath;
5142+
process.env.SSI_TEST_RECIPE_CAPTURE_FILE = captureFile;
5143+
5144+
try {
5145+
const { summary, runtimeError } = await runFixtureMatrix({
5146+
id: 'discovery-overlay-matrix',
5147+
fixtureRoot: workspace.fixtureRoot,
5148+
outputDirectory: workspace.outputDirectory,
5149+
staticSiteImporterPath: workspace.staticSiteImporter,
5150+
blocksEnginePhpTransformerPath: transformerPath,
5151+
blocksEnginePhpTransformerReference: reference,
5152+
run: true,
5153+
batchSize: 1,
5154+
concurrency: 1,
5155+
visualParity: false,
5156+
});
5157+
5158+
assert.equal(runtimeError, null);
5159+
const recipes = JSON.parse(readFileSync(captureFile, 'utf8'));
5160+
const discoveryRecipe = recipes.find((recipe) => recipe.workflow.steps.some((step) => step.args?.some((arg) => arg.includes('plan-artifact-dependencies'))));
5161+
const importRecipe = JSON.parse(readFileSync(summary.runtime.batches[0].recipe_file, 'utf8'));
5162+
const expectedOverlay = {
5163+
kind: 'composer-package',
5164+
package: 'automattic/blocks-engine-php-transformer',
5165+
consumer: 'static-site-importer',
5166+
source: transformerPath,
5167+
reference,
5168+
};
5169+
5170+
assert.deepEqual(discoveryRecipe.inputs.dependency_overlays, [expectedOverlay]);
5171+
assert.deepEqual(importRecipe.inputs.dependency_overlays, [expectedOverlay]);
5172+
} finally {
5173+
restoreConcurrencyEnv(snapshot);
5174+
}
5175+
});
5176+
51065177
test('runFixtureMatrix aggregates batch results order-independently of completion order', async () => {
51075178
const snapshot = snapshotConcurrencyEnv();
51085179
const workspace = setupConcurrencyWorkspace('ssi-concurrency-order-', 4);

0 commit comments

Comments
 (0)