diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 85f0936..77e31b0 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -423,6 +423,13 @@ Recently cut: so runtime owner repos can consume split target, move set, contract input, forbidden import, and external proof shape without trusting only ad hoc smoke script checks. +- Plugin, provider, and conformance handoff documents are now schema-backed as + `gamecult.eve.plugin_handoff.v1`, `gamecult.eve.provider_handoff.v1`, and + `gamecult.eve.conformance_handoff.v1`. The parity runner validates the + documents, exports the schema catalog entries, and owner-consumer smokes now + demand those contracts explicitly, so Sai, Norn, EvePlugins, Aetheria, and + EveConformance can consume handoff shape without depending on Eve's local + smoke script folklore. - EveElectron now has a pending split target and handoff at `runtimes/incubating/eve-electron/eveelectron-split-handoff.json`. It does claim a provider-agnostic Electron shell surface-tree projection and command diff --git a/docs/parity-testing-harness.md b/docs/parity-testing-harness.md index 25a4c8f..99c230f 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -220,6 +220,10 @@ Runtime split handoff documents themselves use `gamecult.eve.runtime_split_handoff.v1`; the parity runner validates the handoff document shape and exports any `splitHandoffErrors` on the runtime record. +Plugin, provider, and EveConformance handoff documents are also schema-backed: +`gamecult.eve.plugin_handoff.v1`, `gamecult.eve.provider_handoff.v1`, and +`gamecult.eve.conformance_handoff.v1` are exported through the schema catalog. +Owner consumer smokes assert those schemas before trusting handoff paths. After generating the parity report, `run-parity-harness.ps1` copies `artifacts/conformance/latest` into diff --git a/schemas/gamecult.eve.conformance_handoff.v1.schema.json b/schemas/gamecult.eve.conformance_handoff.v1.schema.json new file mode 100644 index 0000000..ed6f306 --- /dev/null +++ b/schemas/gamecult.eve.conformance_handoff.v1.schema.json @@ -0,0 +1,98 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "gamecult.eve.conformance_handoff.v1", + "title": "Eve Conformance Handoff", + "type": "object", + "required": [ + "schema", + "splitTarget", + "ownerRepo", + "currentHostRepo", + "authorityBoundary", + "moveSets", + "contractInputs", + "forbiddenImports", + "requiredExternalProofs" + ], + "properties": { + "schema": { + "const": "gamecult.eve.conformance_handoff.v1" + }, + "splitTarget": { + "type": "string", + "minLength": 1 + }, + "ownerRepo": { + "type": "string", + "minLength": 1 + }, + "currentHostRepo": { + "type": "string", + "minLength": 1 + }, + "authorityBoundary": { + "type": "string", + "minLength": 1 + }, + "moveSets": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "id", + "destinationOwner", + "currentPaths", + "replacementProof" + ], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "destinationOwner": { + "type": "string", + "minLength": 1 + }, + "currentPaths": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "replacementProof": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": true + } + }, + "contractInputs": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "forbiddenImports": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "requiredExternalProofs": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + } + }, + "additionalProperties": true +} diff --git a/schemas/gamecult.eve.plugin_handoff.v1.schema.json b/schemas/gamecult.eve.plugin_handoff.v1.schema.json new file mode 100644 index 0000000..1da016d --- /dev/null +++ b/schemas/gamecult.eve.plugin_handoff.v1.schema.json @@ -0,0 +1,118 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "gamecult.eve.plugin_handoff.v1", + "title": "Eve Plugin Handoff", + "type": "object", + "required": [ + "schema", + "pluginId", + "ownerRepo", + "currentHostRepo", + "splitTarget", + "manifestPath", + "advertisementPath", + "abiFixturePath", + "authorityBoundary", + "moveSets", + "contractInputs", + "forbiddenImports", + "requiredExternalProofs" + ], + "properties": { + "schema": { + "const": "gamecult.eve.plugin_handoff.v1" + }, + "pluginId": { + "type": "string", + "minLength": 1 + }, + "ownerRepo": { + "type": "string", + "minLength": 1 + }, + "currentHostRepo": { + "type": "string", + "minLength": 1 + }, + "splitTarget": { + "type": "string", + "minLength": 1 + }, + "manifestPath": { + "type": "string", + "minLength": 1 + }, + "advertisementPath": { + "type": "string", + "minLength": 1 + }, + "abiFixturePath": { + "type": "string", + "minLength": 1 + }, + "authorityBoundary": { + "type": "string", + "minLength": 1 + }, + "moveSets": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "id", + "destinationOwner", + "currentPaths", + "replacementProof" + ], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "destinationOwner": { + "type": "string", + "minLength": 1 + }, + "currentPaths": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "replacementProof": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": true + } + }, + "contractInputs": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "forbiddenImports": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "requiredExternalProofs": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + } + }, + "additionalProperties": true +} diff --git a/schemas/gamecult.eve.provider_handoff.v1.schema.json b/schemas/gamecult.eve.provider_handoff.v1.schema.json new file mode 100644 index 0000000..a78400c --- /dev/null +++ b/schemas/gamecult.eve.provider_handoff.v1.schema.json @@ -0,0 +1,117 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "gamecult.eve.provider_handoff.v1", + "title": "Eve Provider Handoff", + "type": "object", + "required": [ + "schema", + "providerId", + "ownerRepo", + "currentHostRepo", + "advertisementPath", + "scenarioPath", + "fixtureIds", + "authorityBoundary", + "moveSets", + "contractInputs", + "forbiddenImports", + "requiredExternalProofs" + ], + "properties": { + "schema": { + "const": "gamecult.eve.provider_handoff.v1" + }, + "providerId": { + "type": "string", + "minLength": 1 + }, + "ownerRepo": { + "type": "string", + "minLength": 1 + }, + "currentHostRepo": { + "type": "string", + "minLength": 1 + }, + "advertisementPath": { + "type": "string", + "minLength": 1 + }, + "scenarioPath": { + "type": "string", + "minLength": 1 + }, + "fixtureIds": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "authorityBoundary": { + "type": "string", + "minLength": 1 + }, + "moveSets": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "id", + "destinationOwner", + "currentPaths", + "replacementProof" + ], + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "destinationOwner": { + "type": "string", + "minLength": 1 + }, + "currentPaths": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "replacementProof": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": true + } + }, + "contractInputs": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "forbiddenImports": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + }, + "requiredExternalProofs": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1 + } + } + }, + "additionalProperties": true +} diff --git a/scripts/run-aetheria-conformance-consumer-smoke.ps1 b/scripts/run-aetheria-conformance-consumer-smoke.ps1 index e47b108..4c2c567 100644 --- a/scripts/run-aetheria-conformance-consumer-smoke.ps1 +++ b/scripts/run-aetheria-conformance-consumer-smoke.ps1 @@ -38,6 +38,7 @@ Push-Location $AetheriaRoot try { node $consumerScript $consumerExport ` --expect-pack provider ` + --expect-schema gamecult.eve.provider_handoff.v1 ` --expect-fixture aetheria-world ` --expect-provider aetheria ` --expect-scenario aetheria-world-command-replay ` diff --git a/scripts/run-conformance-consumer-smoke.ps1 b/scripts/run-conformance-consumer-smoke.ps1 index 1436fd2..688aed0 100644 --- a/scripts/run-conformance-consumer-smoke.ps1 +++ b/scripts/run-conformance-consumer-smoke.ps1 @@ -33,6 +33,7 @@ try { node .\tools\conformance\consume-export.mjs $consumerExport ` --expect-capability-matrix ` --expect-schema gamecult.eve.conformance_export.v1 ` + --expect-schema gamecult.eve.conformance_handoff.v1 ` --expect-schema gamecult.eve.capability_matrix.v1 ` --expect-schema gamecult.eve.command_receipt.v1 ` --expect-schema gamecult.eve.plugin_receipt.v1 ` @@ -41,6 +42,8 @@ try { --expect-schema gamecult.eve.runtime_release_request.v1 ` --expect-schema gamecult.eve.runtime_capture_request.v1 ` --expect-schema gamecult.eve.runtime_split_handoff.v1 ` + --expect-schema gamecult.eve.plugin_handoff.v1 ` + --expect-schema gamecult.eve.provider_handoff.v1 ` --expect-capability-gap runtime:Fensalir:direct2d:capture:missing ` --expect-world-lowering-coverage aetheria:aetheria.daemon.game:web-reference:claimed:Eve:web ` --expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-uitoolkit:claimed:EveUnity:unity-uitoolkit ` diff --git a/scripts/run-eveconformance-handoff-smoke.ps1 b/scripts/run-eveconformance-handoff-smoke.ps1 index ca5694c..04a6263 100644 --- a/scripts/run-eveconformance-handoff-smoke.ps1 +++ b/scripts/run-eveconformance-handoff-smoke.ps1 @@ -75,18 +75,21 @@ foreach ($contract in @( "gamecult.eve.command.v1", "gamecult.eve.command_receipt.v1", "gamecult.eve.conformance_export.v1", + "gamecult.eve.conformance_handoff.v1", "gamecult.eve.capability_matrix.v1", "gamecult.eve.conformance_fixture.v1", "gamecult.eve.provider_scenario.v1", "gamecult.eve.plugin.v1", "gamecult.eve.plugin_receipt.v1", "gamecult.eve.plugin_advertisement.v1", + "gamecult.eve.plugin_handoff.v1", "gamecult.eve.plugin_abi_fixture.v1", "gamecult.eve.plugin_abi.request.v1", "gamecult.eve.plugin_abi.response.v1", "gamecult.eve.runtime_release_request.v1", "gamecult.eve.runtime_capture_request.v1", "gamecult.eve.provider_advertisement.v1", + "gamecult.eve.provider_handoff.v1", "gamecult.eve.runtime_capability.v1", "gamecult.eve.runtime_split_handoff.v1" )) { @@ -114,16 +117,19 @@ if (Test-Path -LiteralPath (Join-Path $absoluteExportDirectory "index.json")) { } foreach ($schemaId in @( "gamecult.eve.conformance_export.v1", + "gamecult.eve.conformance_handoff.v1", "gamecult.eve.capability_matrix.v1", "gamecult.eve.conformance_fixture.v1", "gamecult.eve.provider_scenario.v1", "gamecult.eve.provider_advertisement.v1", + "gamecult.eve.provider_handoff.v1", "gamecult.eve.runtime_capability.v1", "gamecult.eve.runtime_split_handoff.v1", "gamecult.eve.command_receipt.v1", "gamecult.eve.plugin.v1", "gamecult.eve.plugin_receipt.v1", "gamecult.eve.plugin_advertisement.v1", + "gamecult.eve.plugin_handoff.v1", "gamecult.eve.plugin_abi_fixture.v1", "gamecult.eve.plugin_abi.request.v1", "gamecult.eve.plugin_abi.response.v1", diff --git a/scripts/run-plugin-owner-conformance-consumer-smoke.ps1 b/scripts/run-plugin-owner-conformance-consumer-smoke.ps1 index 9356b91..22ce168 100644 --- a/scripts/run-plugin-owner-conformance-consumer-smoke.ps1 +++ b/scripts/run-plugin-owner-conformance-consumer-smoke.ps1 @@ -34,6 +34,7 @@ node $consumerScript $consumerExport ` --expect-pack plugin ` --expect-schema gamecult.eve.plugin.v1 ` --expect-schema gamecult.eve.plugin_advertisement.v1 ` + --expect-schema gamecult.eve.plugin_handoff.v1 ` --expect-schema gamecult.eve.plugin_abi_fixture.v1 ` --expect-schema gamecult.eve.plugin_abi.request.v1 ` --expect-schema gamecult.eve.plugin_abi.response.v1 ` diff --git a/tools/conformance/eveconformance-handoff.json b/tools/conformance/eveconformance-handoff.json index 52815e0..4785ef1 100644 --- a/tools/conformance/eveconformance-handoff.json +++ b/tools/conformance/eveconformance-handoff.json @@ -20,16 +20,19 @@ "destinationOwner": "EveConformance", "currentPaths": [ "schemas/gamecult.eve.conformance_export.v1.schema.json", + "schemas/gamecult.eve.conformance_handoff.v1.schema.json", "schemas/gamecult.eve.capability_matrix.v1.schema.json", "schemas/gamecult.eve.conformance_fixture.v1.schema.json", "schemas/gamecult.eve.provider_scenario.v1.schema.json", "schemas/gamecult.eve.provider_advertisement.v1.schema.json", + "schemas/gamecult.eve.provider_handoff.v1.schema.json", "schemas/gamecult.eve.runtime_capability.v1.schema.json", "schemas/gamecult.eve.runtime_split_handoff.v1.schema.json", "schemas/gamecult.eve.command_receipt.v1.schema.json", "schemas/gamecult.eve.plugin.v1.schema.json", "schemas/gamecult.eve.plugin_receipt.v1.schema.json", "schemas/gamecult.eve.plugin_advertisement.v1.schema.json", + "schemas/gamecult.eve.plugin_handoff.v1.schema.json", "schemas/gamecult.eve.plugin_abi_fixture.v1.schema.json", "schemas/gamecult.eve.plugin_abi.request.v1.schema.json", "schemas/gamecult.eve.plugin_abi.response.v1.schema.json", @@ -69,18 +72,21 @@ "gamecult.eve.command.v1", "gamecult.eve.command_receipt.v1", "gamecult.eve.conformance_export.v1", + "gamecult.eve.conformance_handoff.v1", "gamecult.eve.capability_matrix.v1", "gamecult.eve.conformance_fixture.v1", "gamecult.eve.provider_scenario.v1", "gamecult.eve.plugin.v1", "gamecult.eve.plugin_receipt.v1", "gamecult.eve.plugin_advertisement.v1", + "gamecult.eve.plugin_handoff.v1", "gamecult.eve.plugin_abi_fixture.v1", "gamecult.eve.plugin_abi.request.v1", "gamecult.eve.plugin_abi.response.v1", "gamecult.eve.runtime_release_request.v1", "gamecult.eve.runtime_capture_request.v1", "gamecult.eve.provider_advertisement.v1", + "gamecult.eve.provider_handoff.v1", "gamecult.eve.runtime_capability.v1", "gamecult.eve.runtime_split_handoff.v1" ], diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index da709bf..d17ce2c 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -18,12 +18,15 @@ "gamecult.eve.plugin_abi.response.v1": "schemas/gamecult.eve.plugin_abi.response.v1.schema.json", "gamecult.eve.plugin_abi_fixture.v1": "schemas/gamecult.eve.plugin_abi_fixture.v1.schema.json", "gamecult.eve.plugin_advertisement.v1": "schemas/gamecult.eve.plugin_advertisement.v1.schema.json", + "gamecult.eve.plugin_handoff.v1": "schemas/gamecult.eve.plugin_handoff.v1.schema.json", "gamecult.eve.runtime_capability.v1": "schemas/gamecult.eve.runtime_capability.v1.schema.json", "gamecult.eve.runtime_split_handoff.v1": "schemas/gamecult.eve.runtime_split_handoff.v1.schema.json", "gamecult.eve.runtime_release_request.v1": "schemas/gamecult.eve.runtime_release_request.v1.schema.json", "gamecult.eve.runtime_capture_request.v1": "schemas/gamecult.eve.runtime_capture_request.v1.schema.json", "gamecult.eve.provider_scenario.v1": "schemas/gamecult.eve.provider_scenario.v1.schema.json", - "gamecult.eve.provider_advertisement.v1": "schemas/gamecult.eve.provider_advertisement.v1.schema.json" + "gamecult.eve.provider_advertisement.v1": "schemas/gamecult.eve.provider_advertisement.v1.schema.json", + "gamecult.eve.provider_handoff.v1": "schemas/gamecult.eve.provider_handoff.v1.schema.json", + "gamecult.eve.conformance_handoff.v1": "schemas/gamecult.eve.conformance_handoff.v1.schema.json" }, "conformancePacks": [ { diff --git a/tools/parity/run-parity.mjs b/tools/parity/run-parity.mjs index 2e5ac8b..8f1fe18 100644 --- a/tools/parity/run-parity.mjs +++ b/tools/parity/run-parity.mjs @@ -28,11 +28,15 @@ const pluginResults = await Promise.all((manifest.pluginManifests || []).map(plu const providerResults = await Promise.all((manifest.providerAdvertisements || []).map(provider => evaluateProvider(provider, fixtureResults, pluginResults))); const runtimeResults = await Promise.all(manifest.runtimes.map(runtime => evaluateRuntime(runtime, fixtureResults))); const splitTargetResults = evaluateSplitTargets(manifest.splitTargets || [], runtimeResults); +const conformanceHandoffErrors = await validateConformanceHandoff(manifest.repoStrategy?.conformanceHandoffPath || ""); const report = { schema: "gamecult.eve.parity_report.v1", generatedAt: new Date().toISOString(), manifest: path.relative(repoRoot, manifestPath).replaceAll("\\", "/"), - repoStrategy: manifest.repoStrategy || {}, + repoStrategy: { + ...(manifest.repoStrategy || {}), + conformanceHandoffErrors, + }, conformancePacks: manifest.conformancePacks || [], responsiveCases: manifest.responsiveCases || [], summary: summarize(fixtureResults, runtimeResults, pluginResults, providerResults, splitTargetResults), @@ -60,7 +64,9 @@ console.log(`Parity report: ${path.relative(repoRoot, path.join(runDirectory, "p console.log(`Conformance export: ${path.relative(repoRoot, path.join(conformanceOutputRoot, "latest", "index.md"))}`); for (const error of conformanceExportErrors) console.error(`Conformance export error: ${error}`); if (report.summary.failedFixtures > 0) process.exitCode = 1; +if (report.summary.failedPlugins > 0) process.exitCode = 1; if (report.summary.failedProviders > 0) process.exitCode = 1; +if (conformanceHandoffErrors.length > 0) process.exitCode = 1; if (conformanceExportErrors.length > 0) process.exitCode = 1; async function evaluateFixture(fixture) { @@ -452,6 +458,19 @@ async function validateRuntimeSplitHandoff(runtime, splitHandoffPath) { ); } +async function validateConformanceHandoff(handoffPath) { + if (!handoffPath) return ["handoffPath:missing"]; + return validateJsonDocument( + manifest.schemas?.["gamecult.eve.conformance_handoff.v1"], + handoffPath, + { + schema: "gamecult.eve.conformance_handoff.v1", + splitTarget: "EveConformance", + ownerRepo: "EveConformance", + }, + ); +} + function normalizePath(candidate) { return candidate.replaceAll("\\", "/").replace(/^\.\//, ""); } @@ -482,6 +501,7 @@ async function evaluatePlugin(plugin, fixtureResults) { const abiOperations = await readPluginAbiOperations(plugin.abiFixturePath); const abiOperationContracts = await readPluginAbiOperationContracts(plugin.abiFixturePath); const abiErrors = await validatePluginAbiFixture(plugin); + const handoffErrors = await validatePluginHandoff(plugin); const status = missingPaths.length ? "missing-body" : missingRequiredFixtures.length @@ -496,9 +516,11 @@ async function evaluatePlugin(plugin, fixtureResults) { ? "invalid-plugin-runtime-boundary" : abiErrors.length ? "invalid-plugin-abi-fixture" - : plugin.kind === "incubating" - ? "incubating" - : "external-owner-planned"; + : handoffErrors.length + ? "invalid-plugin-handoff" + : plugin.kind === "incubating" + ? "incubating" + : "external-owner-planned"; return { pluginId: plugin.pluginId, @@ -518,6 +540,7 @@ async function evaluatePlugin(plugin, fixtureResults) { manifestPath: plugin.manifestPath || "", abiFixturePath: plugin.abiFixturePath || "", handoffPath: plugin.handoffPath || "", + handoffErrors, abiOperations, abiOperationContracts, advertisementSchemaPath: plugin.advertisementSchemaPath || "", @@ -532,6 +555,19 @@ async function evaluatePlugin(plugin, fixtureResults) { }; } +async function validatePluginHandoff(plugin) { + if (!plugin.handoffPath) return []; + return validateJsonDocument( + manifest.schemas?.["gamecult.eve.plugin_handoff.v1"], + plugin.handoffPath, + { + schema: "gamecult.eve.plugin_handoff.v1", + pluginId: plugin.pluginId, + ownerRepo: plugin.ownerRepo, + }, + ); +} + async function readPluginAbiOperations(abiFixturePath) { if (!abiFixturePath) return []; try { @@ -703,6 +739,7 @@ async function evaluateProvider(provider, fixtureResults, pluginResults) { const missingCommands = (provider.expectedCommands || []).filter(command => !commandIds.includes(command)); const scenarioErrors = await validateProviderScenario(provider, advertisement, fixtureResults, advertisedSchemaIds, surfaceIds, commandIds); const scenario = scenarioErrors.length || !provider.scenarioPath ? null : await readJsonDocument(provider.scenarioPath); + const handoffErrors = await validateProviderHandoff(provider); const status = missingPaths.length ? "missing-body" : missingRequiredFixtures.length @@ -715,7 +752,9 @@ async function evaluateProvider(provider, fixtureResults, pluginResults) { ? "capability-gap" : scenarioErrors.length ? "invalid-provider-scenario" - : "advertised"; + : handoffErrors.length + ? "invalid-provider-handoff" + : "advertised"; return { providerId: provider.providerId, @@ -730,6 +769,7 @@ async function evaluateProvider(provider, fixtureResults, pluginResults) { advertisementPath: provider.advertisementPath || "", scenarioPath: provider.scenarioPath || "", handoffPath: provider.handoffPath || "", + handoffErrors, advertisementErrors, scenarioErrors, expectedPaths, @@ -758,6 +798,19 @@ async function evaluateProvider(provider, fixtureResults, pluginResults) { }; } +async function validateProviderHandoff(provider) { + if (!provider.handoffPath) return []; + return validateJsonDocument( + manifest.schemas?.["gamecult.eve.provider_handoff.v1"], + provider.handoffPath, + { + schema: "gamecult.eve.provider_handoff.v1", + providerId: provider.providerId, + ownerRepo: provider.ownerRepo, + }, + ); +} + function collectProviderPluginRequirements(advertisement) { const requirements = []; for (const surface of advertisement.surfaces || []) { @@ -1687,6 +1740,7 @@ function summarize(fixtures, runtimes, plugins, providers, splitTargets) { failedFixtures: fixtures.filter(fixture => fixture.status !== "pass").length, totalPlugins: plugins.length, healthyPlugins: plugins.filter(plugin => plugin.status === "incubating" || plugin.status === "external-owner-planned").length, + failedPlugins: plugins.filter(plugin => plugin.status !== "incubating" && plugin.status !== "external-owner-planned").length, totalProviders: providers.length, advertisedProviders: providers.filter(provider => provider.status === "advertised").length, failedProviders: providers.filter(provider => provider.status !== "advertised").length, @@ -1784,6 +1838,7 @@ function buildConformanceExport(report) { manifestPath: plugin.manifestPath, advertisementPath: plugin.advertisementPath, handoffPath: plugin.handoffPath, + handoffErrors: plugin.handoffErrors || [], handoffExportPath: makeHandoffExportPath("plugin", plugin.pluginId, plugin.handoffPath), abiFixturePath: plugin.abiFixturePath, abiOperations: plugin.abiOperations || [], @@ -1799,6 +1854,7 @@ function buildConformanceExport(report) { advertisementPath: provider.advertisementPath, scenarioPath: provider.scenarioPath, handoffPath: provider.handoffPath, + handoffErrors: provider.handoffErrors || [], handoffExportPath: makeHandoffExportPath("provider", provider.providerId, provider.handoffPath), scenarioId: provider.scenarioId, receiptStates: provider.scenarioReceiptStates || [], @@ -1938,6 +1994,7 @@ function collectCapabilityGaps(report) { ...(plugin.advertisementErrors || []).map(id => `advertisement:${id}`), ...(plugin.runtimeBoundaryErrors || []).map(id => `runtime-boundary:${id}`), ...(plugin.abiErrors || []).map(id => `abi:${id}`), + ...(plugin.handoffErrors || []).map(id => `handoff:${id}`), ]) { addGap({ kind: "plugin", @@ -1961,6 +2018,7 @@ function collectCapabilityGaps(report) { ...(provider.missingSurfaceKinds || []).map(id => `surface-kind:${id}`), ...(provider.missingCommands || []).map(id => `command:${id}`), ...(provider.pluginRequirementErrors || []).map(id => `plugin:${id}`), + ...(provider.handoffErrors || []).map(id => `handoff:${id}`), ]) { addGap({ kind: "provider", @@ -2037,6 +2095,16 @@ function collectCapabilityGaps(report) { } } + for (const error of report.repoStrategy?.conformanceHandoffErrors || []) { + addGap({ + kind: "conformance", + ownerRepo: "EveConformance", + subjectId: "EveConformance", + gap: `handoff:${error}`, + severity: "blocker", + }); + } + return gaps; }