Export runtime plugin projection gaps
This commit is contained in:
parent
8d9f78e107
commit
acc3ed6945
6 changed files with 150 additions and 1 deletions
|
|
@ -308,6 +308,11 @@ Recently cut:
|
|||
`plugins[].abiOperationContracts`. Sai and Norn can consume operation-level
|
||||
`input` and `expect` contracts for `describe`, `validate`, `project`,
|
||||
`lower`, `measure`, and `apply` without reading Eve-local fixture files.
|
||||
- Runtime/plugin projection gaps are now exported as
|
||||
`runtimePluginProjectionGaps[]`. The runtime-owner smoke asserts Unity and
|
||||
Electron plugin gaps as typed records, so missing Sai, Norn, or TeX projection
|
||||
support is owned by the runtime split target and does not imply those plugin
|
||||
semantics should move into Eve core or Aetheria product code.
|
||||
- EveUnity now has a pending Unity scene handoff at
|
||||
`runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json`.
|
||||
The active `unity-uitoolkit` runtime remains a semantic UI surface lowerer;
|
||||
|
|
|
|||
|
|
@ -113,6 +113,12 @@ The same facts are also exported as `worldSurfaceLoweringGaps[]` so runtime
|
|||
owners can consume provider id, surface id, target id, owner repo, runtime id,
|
||||
split target, and runtime status as typed fields instead of parsing the generic
|
||||
gap text.
|
||||
Declared runtime/plugin projection gaps are exported as
|
||||
`runtimePluginProjectionGaps[]`. These records carry runtime id, runtime owner,
|
||||
split target, plugin id, reason, severity, and fixture lists so EveUnity,
|
||||
EveElectron, Sai, Norn, and TeX owners can distinguish "runtime cannot project
|
||||
this plugin yet" from "plugin semantics belong in Eve core." Consumer smokes can
|
||||
assert them with `--expect-runtime-plugin-gap <runtimeId:pluginId:ownerRepo>`.
|
||||
|
||||
The conformance export includes runtime `capabilityManifestPath` and
|
||||
`capabilityManifestErrors` fields so external consumers can distinguish a
|
||||
|
|
|
|||
|
|
@ -95,6 +95,58 @@
|
|||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"runtimePluginProjectionGaps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"runtimeId",
|
||||
"runtimeStatus",
|
||||
"ownerRepo",
|
||||
"splitTarget",
|
||||
"pluginId",
|
||||
"severity",
|
||||
"reason",
|
||||
"requiredFixtures",
|
||||
"pluginFixtures"
|
||||
],
|
||||
"properties": {
|
||||
"runtimeId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"runtimeStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"ownerRepo": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"splitTarget": {
|
||||
"type": "string"
|
||||
},
|
||||
"pluginId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"requiredFixtures": {
|
||||
"type": "array"
|
||||
},
|
||||
"pluginFixtures": {
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"worldSurfaceLoweringGaps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,11 @@ node $consumerScript $consumerExport `
|
|||
--expect-runtime-world-target unity-uitoolkit:unity-uitoolkit `
|
||||
--expect-runtime-world-field unity-uitoolkit:unity-uitoolkit:supportLevel:ui-toolkit-semantic-command-surface `
|
||||
--expect-runtime-world-field unity-uitoolkit:unity-uitoolkit:ownership:runtime-lowers-provider-world-surface-without-owning-world-state `
|
||||
--expect-runtime-plugin-gap unity-uitoolkit:tex.math:EveUnity `
|
||||
--expect-runtime-plugin-gap unity-scene:sai.vn:EveUnity `
|
||||
--expect-runtime-plugin-gap unity-scene:norn.graph:EveUnity `
|
||||
--expect-runtime-plugin-gap electron-shell:sai.vn:EveElectron `
|
||||
--expect-runtime-plugin-gap electron-shell:norn.graph:EveElectron `
|
||||
--expect-runtime-handoff unity-uitoolkit `
|
||||
--expect-runtime-command-schema web:gamecult.eve.command.v1 `
|
||||
--expect-runtime-command-schema windows-flutter:gamecult.eve.command.v1 `
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ if (!exportDirectory) {
|
|||
" --expect-split-target <id>",
|
||||
" --expect-capability-matrix",
|
||||
" --expect-capability-gap <substring>",
|
||||
" --expect-runtime-plugin-gap <runtimeId:pluginId:ownerRepo>",
|
||||
" --expect-world-lowering-gap <providerId:surfaceId:targetId:ownerRepo:runtimeId>",
|
||||
" --expect-conformance-handoff",
|
||||
" --expect-plugin-operation <pluginId:operation>",
|
||||
|
|
@ -122,6 +123,7 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
const runtimes = mergeRuntimeRecords(Array.isArray(index.runtimes) ? index.runtimes : [], exportedRuntimeTargets);
|
||||
const splitTargets = Array.isArray(index.splitTargets) ? index.splitTargets : [];
|
||||
const capabilityGaps = Array.isArray(index.capabilityGaps) ? index.capabilityGaps : [];
|
||||
const runtimePluginProjectionGaps = Array.isArray(index.runtimePluginProjectionGaps) ? index.runtimePluginProjectionGaps : [];
|
||||
const worldSurfaceLoweringGaps = Array.isArray(index.worldSurfaceLoweringGaps) ? index.worldSurfaceLoweringGaps : [];
|
||||
|
||||
validatePluginRecords(plugins, errors);
|
||||
|
|
@ -139,12 +141,28 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
validateCapabilityMatrix(index.capabilityMatrix, packs, plugins, providers, runtimes, splitTargets, errors);
|
||||
}
|
||||
validateCapabilityGaps(index.capabilityGaps, errors);
|
||||
validateRuntimePluginProjectionGaps(index.runtimePluginProjectionGaps, errors);
|
||||
validateWorldSurfaceLoweringGaps(index.worldSurfaceLoweringGaps, errors);
|
||||
for (const expectedGap of expectations.capabilityGaps) {
|
||||
if (!capabilityGaps.some(gap => capabilityGapText(gap).includes(expectedGap))) {
|
||||
errors.push(`capabilityGaps:${expectedGap}:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.runtimePluginGaps) {
|
||||
const gap = runtimePluginProjectionGaps.find(candidate =>
|
||||
candidate.runtimeId === expectation.runtimeId &&
|
||||
candidate.pluginId === expectation.pluginId);
|
||||
if (!gap) {
|
||||
errors.push(`runtimePluginProjectionGaps:${expectation.runtimeId}:${expectation.pluginId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (gap.ownerRepo !== expectation.ownerRepo) {
|
||||
errors.push(`runtimePluginProjectionGaps:${expectation.runtimeId}:${expectation.pluginId}:ownerRepo:expected ${expectation.ownerRepo} got ${gap.ownerRepo || ""}`);
|
||||
}
|
||||
if (!gap.reason) {
|
||||
errors.push(`runtimePluginProjectionGaps:${expectation.runtimeId}:${expectation.pluginId}:reason:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.worldLoweringGaps) {
|
||||
const gap = worldSurfaceLoweringGaps.find(candidate =>
|
||||
candidate.providerId === expectation.providerId &&
|
||||
|
|
@ -577,6 +595,21 @@ function validateCapabilityGaps(gaps, errors) {
|
|||
}
|
||||
}
|
||||
|
||||
function validateRuntimePluginProjectionGaps(gaps, errors) {
|
||||
if (!Array.isArray(gaps)) {
|
||||
errors.push("runtimePluginProjectionGaps:missing");
|
||||
return;
|
||||
}
|
||||
for (const [index, gap] of gaps.entries()) {
|
||||
for (const field of ["runtimeId", "ownerRepo", "pluginId", "severity", "reason"]) {
|
||||
if (!gap?.[field]) errors.push(`runtimePluginProjectionGaps:${index}:${field}:missing`);
|
||||
}
|
||||
for (const field of ["requiredFixtures", "pluginFixtures"]) {
|
||||
if (!Array.isArray(gap?.[field])) errors.push(`runtimePluginProjectionGaps:${index}:${field}:expected array`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateWorldSurfaceLoweringGaps(gaps, errors) {
|
||||
if (!Array.isArray(gaps)) {
|
||||
errors.push("worldSurfaceLoweringGaps:missing");
|
||||
|
|
@ -699,6 +732,7 @@ function parseArguments(args) {
|
|||
splitTargetProofs: [],
|
||||
capabilityMatrix: false,
|
||||
capabilityGaps: [],
|
||||
runtimePluginGaps: [],
|
||||
worldLoweringGaps: [],
|
||||
conformanceHandoff: false,
|
||||
};
|
||||
|
|
@ -738,6 +772,7 @@ function parseArguments(args) {
|
|||
["--expect-split-target-blocker", expectations.splitTargetBlockers],
|
||||
["--expect-split-target-proof", expectations.splitTargetProofs],
|
||||
["--expect-capability-gap", expectations.capabilityGaps],
|
||||
["--expect-runtime-plugin-gap", expectations.runtimePluginGaps],
|
||||
["--expect-world-lowering-gap", expectations.worldLoweringGaps],
|
||||
]);
|
||||
|
||||
|
|
@ -817,6 +852,8 @@ function parseArguments(args) {
|
|||
target.push(parseSplitTargetExpectation(value, "proof"));
|
||||
} else if (option === "--expect-world-lowering-gap") {
|
||||
target.push(parseWorldLoweringGapExpectation(value));
|
||||
} else if (option === "--expect-runtime-plugin-gap") {
|
||||
target.push(parseRuntimePluginGapExpectation(value));
|
||||
} else {
|
||||
target.push(value);
|
||||
}
|
||||
|
|
@ -841,6 +878,19 @@ function parseWorldLoweringGapExpectation(value) {
|
|||
};
|
||||
}
|
||||
|
||||
function parseRuntimePluginGapExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length !== 3 || parts.some(part => !part)) {
|
||||
console.error(`Expected runtime plugin projection gap in <runtimeId:pluginId:ownerRepo> form, got: ${value}`);
|
||||
process.exit(2);
|
||||
}
|
||||
return {
|
||||
runtimeId: parts[0],
|
||||
pluginId: parts[1],
|
||||
ownerRepo: parts[2],
|
||||
};
|
||||
}
|
||||
|
||||
function parsePluginExpectation(value, field) {
|
||||
const separator = value.indexOf(":");
|
||||
if (separator <= 0 || separator === value.length - 1) {
|
||||
|
|
|
|||
|
|
@ -1607,6 +1607,7 @@ function buildConformanceExport(report) {
|
|||
conformanceHandoffExportPath: makeHandoffExportPath("conformance", "EveConformance", report.repoStrategy.conformanceHandoffPath || ""),
|
||||
packs,
|
||||
capabilityMatrix: buildCapabilityMatrix(report),
|
||||
runtimePluginProjectionGaps: collectRuntimePluginProjectionGaps(report),
|
||||
worldSurfaceLoweringGaps: collectWorldSurfaceLoweringGaps(report),
|
||||
capabilityGaps: collectCapabilityGaps(report),
|
||||
plugins: (report.plugins || []).map(plugin => ({
|
||||
|
|
@ -1828,7 +1829,7 @@ function collectCapabilityGaps(report) {
|
|||
for (const note of runtime.unsupportedPluginNotes || []) {
|
||||
addGap({
|
||||
kind: "runtime",
|
||||
ownerRepo: runtime.ownerRepo,
|
||||
ownerRepo: resolveRuntimeProjectionOwnerRepo(runtime),
|
||||
subjectId: runtime.id,
|
||||
gap: `unsupported-plugin:${note}`,
|
||||
severity: "declared-gap",
|
||||
|
|
@ -1862,6 +1863,36 @@ function collectCapabilityGaps(report) {
|
|||
return gaps;
|
||||
}
|
||||
|
||||
function collectRuntimePluginProjectionGaps(report) {
|
||||
const gaps = [];
|
||||
for (const runtime of report.runtimes || []) {
|
||||
for (const plugin of runtime.unsupportedPlugins || []) {
|
||||
if (!plugin.pluginId) continue;
|
||||
gaps.push({
|
||||
runtimeId: runtime.id,
|
||||
runtimeStatus: runtime.status || "",
|
||||
ownerRepo: resolveRuntimeProjectionOwnerRepo(runtime),
|
||||
splitTarget: runtime.splitTarget || "",
|
||||
pluginId: plugin.pluginId,
|
||||
severity: "declared-gap",
|
||||
reason: plugin.reason || "unsupported",
|
||||
requiredFixtures: runtime.requiredFixtures || [],
|
||||
pluginFixtures: runtime.pluginFixtures || [],
|
||||
});
|
||||
}
|
||||
}
|
||||
return gaps;
|
||||
}
|
||||
|
||||
function resolveRuntimeProjectionOwnerRepo(runtime) {
|
||||
return runtime.lifecycle?.release?.ownerRepo
|
||||
|| runtime.lifecycle?.test?.ownerRepo
|
||||
|| runtime.lifecycle?.capture?.ownerRepo
|
||||
|| runtime.splitTarget
|
||||
|| runtime.ownerRepo
|
||||
|| "";
|
||||
}
|
||||
|
||||
function collectWorldSurfaceLoweringGaps(report) {
|
||||
const claimedTargets = new Set();
|
||||
const runtimeByWorldTarget = new Map();
|
||||
|
|
|
|||
Loading…
Reference in a new issue