Export runtime plugin projection coverage
This commit is contained in:
parent
b9f7592edf
commit
ca01596b35
11 changed files with 265 additions and 29 deletions
|
|
@ -331,10 +331,16 @@ Recently cut:
|
|||
owners can consume the coverage ledger without walking Eve's nested plugin
|
||||
records.
|
||||
- Provider/plugin requirement coverage is now exported as root
|
||||
`providerPluginRequirementCoverage[]` records. The plugin-owner smoke asserts
|
||||
the Sai VN provider surface requirements for `sai.vn`, `norn.graph`, and
|
||||
`tex.math` as satisfied by their owning plugin repos, keeping provider
|
||||
requirements separate from runtime projection support.
|
||||
`providerPluginRequirementCoverage[]` records with availability. The
|
||||
plugin-owner smoke asserts `sai.vn` as the required VN plugin and
|
||||
`norn.graph` / `tex.math` as `optional-satisfied` nested plugins owned by
|
||||
Norn and EvePlugins. A Sai surface can embed Norn or TeX when those plugins
|
||||
are available, but Sai does not own their graph or math semantics.
|
||||
- Runtime/plugin projection claims are now exported as root
|
||||
`runtimePluginProjectionCoverage[]` records. Runtime-owner smokes assert
|
||||
Unity UI Toolkit support for `sai.vn` and `norn.graph`, its declared TeX gap,
|
||||
and the pending Unity scene/Electron unsupported Sai projection until their
|
||||
generic lowerers exist.
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -141,6 +141,13 @@ 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 same claims are exported as `runtimePluginProjectionCoverage[]`, including
|
||||
positive `supported` projection adapters and declared `unsupported` gaps. This
|
||||
lets runtime and plugin owners consume one ledger for "Unity UI Toolkit supports
|
||||
Sai/Norn projection" and "Unity scene/Electron do not have generic plugin
|
||||
projection yet" instead of walking nested runtime records. Consumer smokes can
|
||||
assert entries with
|
||||
`--expect-runtime-plugin-projection <runtimeId:pluginId:status:ownerRepo>`.
|
||||
|
||||
The conformance export includes runtime `capabilityManifestPath` and
|
||||
`capabilityManifestErrors` fields so external consumers can distinguish a
|
||||
|
|
@ -162,10 +169,12 @@ expect keys. Plugin-owner smokes can assert contracted operations with
|
|||
without walking nested plugin records.
|
||||
Provider-advertised plugin requirements are indexed as root
|
||||
`providerPluginRequirementCoverage[]` records. These join provider id, surface
|
||||
id, plugin id, plugin owner, plugin status, required capabilities, and missing
|
||||
capabilities so provider and plugin owners can consume requirement coverage
|
||||
without reconstructing the provider/plugin join. Consumer smokes can assert
|
||||
them with
|
||||
id, plugin id, plugin owner, plugin status, availability, required
|
||||
capabilities, optional capabilities, and missing capabilities so provider and
|
||||
plugin owners can consume requirement coverage without reconstructing the
|
||||
provider/plugin join. `availability: optional-nested` means the parent surface
|
||||
can host that independent plugin when available; it is not a parent-plugin
|
||||
dependency. Consumer smokes can assert them with
|
||||
`--expect-provider-plugin-requirement <providerId:surfaceId:pluginId:status:pluginOwnerRepo>`.
|
||||
|
||||
The runtime conformance pack is evidence-shaped, not fixture-shaped. Its
|
||||
|
|
|
|||
|
|
@ -316,19 +316,23 @@ Recommended discovery path:
|
|||
|
||||
1. A plugin publishes a plugin advertisement through CultMesh/Odin or ships a
|
||||
local package manifest.
|
||||
2. A provider advertisement names the plugins its surfaces require.
|
||||
2. A provider advertisement names the plugins its surfaces require and the
|
||||
optional nested plugins it can use when available.
|
||||
3. Odin indexes providers and plugins separately.
|
||||
4. A renderer asks Odin for the provider surface and the required plugin
|
||||
manifests.
|
||||
5. The renderer checks local plugin support.
|
||||
6. Unsupported required plugin capability becomes a visible capability gap.
|
||||
7. Optional unsupported capability becomes a visible degraded lowering.
|
||||
7. Unsupported optional nested plugin capability becomes a visible degraded
|
||||
lowering, not a claim that the parent plugin owns that nested semantic.
|
||||
|
||||
Eve's parity harness validates this boundary directly: provider-advertised
|
||||
`surfaces[].requiresPlugins[]` entries must name known plugin manifests, and
|
||||
each `requiredCapabilities` item must be claimed by that plugin manifest. This
|
||||
keeps plugin requirements in the provider advertisement instead of hiding them
|
||||
in runtime lowerers or fixture-only metadata.
|
||||
each `requiredCapabilities` item must be claimed by that plugin manifest. Entries
|
||||
with `availability: optional-nested` are reported separately so a Sai surface can
|
||||
embed Norn or TeX when those plugins are available without making them Sai
|
||||
dependencies. This keeps plugin requirements in the provider advertisement
|
||||
instead of hiding them in runtime lowerers or fixture-only metadata.
|
||||
|
||||
Provider advertisement sketch:
|
||||
|
||||
|
|
@ -344,10 +348,20 @@ Provider advertisement sketch:
|
|||
{
|
||||
"pluginId": "sai.vn",
|
||||
"versionRange": "^0.1.0",
|
||||
"availability": "required",
|
||||
"requiredCapabilities": [
|
||||
"vn.stage",
|
||||
"story.choose"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pluginId": "norn.graph",
|
||||
"versionRange": "^0.1.0",
|
||||
"availability": "optional-nested",
|
||||
"requiredCapabilities": [],
|
||||
"optionalCapabilities": [
|
||||
"embed.norn"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,10 +84,14 @@ Required top-level meanings:
|
|||
and ownership rule. Runtimes use it to decide how to lower the surface and
|
||||
where command intents must return; they do not gain authority over world
|
||||
state because they can render it.
|
||||
- `surfaces[].requiresPlugins`: sidecar plugin capabilities required to render
|
||||
or interact with that surface. Runtimes compare these requirements with their
|
||||
local projection support and report visible capability gaps; they must not
|
||||
import provider or plugin internals to recover missing semantics.
|
||||
- `surfaces[].requiresPlugins`: sidecar plugin capabilities used by the
|
||||
surface. Entries may be `availability: required` or
|
||||
`availability: optional-nested`. Required entries gate full surface
|
||||
interaction. Optional nested entries, such as a Sai-authored scene slotting in
|
||||
Norn or TeX, degrade when unavailable; they do not make the parent plugin own
|
||||
the nested plugin's semantics. Runtimes compare these entries with local
|
||||
projection support and report visible capability gaps; they must not import
|
||||
provider or plugin internals to recover missing semantics.
|
||||
- `commands`: command boundaries the provider accepts.
|
||||
- `routes`: transport, store, or compatibility routes used to reach the
|
||||
semantic address. WebSocket and HTTP routes live here when they still exist.
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"runtimes",
|
||||
"pluginAbiOperationCoverage",
|
||||
"providerPluginRequirementCoverage",
|
||||
"runtimePluginProjectionCoverage",
|
||||
"interactiveWorldSurfaces",
|
||||
"worldSurfaceLoweringCoverage",
|
||||
"commandBoundaryCoverage",
|
||||
|
|
@ -154,6 +155,73 @@
|
|||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"runtimePluginProjectionCoverage": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"runtimeId",
|
||||
"runtimeStatus",
|
||||
"runtimeOwnerRepo",
|
||||
"pluginId",
|
||||
"pluginOwnerRepo",
|
||||
"status",
|
||||
"severity",
|
||||
"capabilities",
|
||||
"requiredFixtures",
|
||||
"pluginFixtures"
|
||||
],
|
||||
"properties": {
|
||||
"runtimeId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"runtimeStatus": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"runtimeOwnerRepo": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"splitTarget": {
|
||||
"type": "string"
|
||||
},
|
||||
"pluginId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"pluginOwnerRepo": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"pluginStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"capabilities": {
|
||||
"type": "array"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"requiredFixtures": {
|
||||
"type": "array"
|
||||
},
|
||||
"pluginFixtures": {
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"splitTargetBlockers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
@ -720,10 +788,12 @@
|
|||
"pluginOwnerRepo",
|
||||
"pluginStatus",
|
||||
"versionRange",
|
||||
"availability",
|
||||
"status",
|
||||
"requiredCapabilities",
|
||||
"optionalCapabilities",
|
||||
"missingRequiredCapabilities"
|
||||
"missingRequiredCapabilities",
|
||||
"missingOptionalCapabilities"
|
||||
],
|
||||
"properties": {
|
||||
"providerId": {
|
||||
|
|
@ -752,6 +822,10 @@
|
|||
"versionRange": {
|
||||
"type": "string"
|
||||
},
|
||||
"availability": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
|
|
@ -764,6 +838,9 @@
|
|||
},
|
||||
"missingRequiredCapabilities": {
|
||||
"type": "array"
|
||||
},
|
||||
"missingOptionalCapabilities": {
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
|
|
|
|||
|
|
@ -201,6 +201,10 @@
|
|||
"versionRange": {
|
||||
"type": "string"
|
||||
},
|
||||
"availability": {
|
||||
"type": "string",
|
||||
"enum": ["required", "optional", "optional-nested"]
|
||||
},
|
||||
"requiredCapabilities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ node $consumerScript $consumerExport `
|
|||
--expect-plugin-abi-operation-coverage norn.graph:describe:contracted:Norn `
|
||||
--expect-plugin-abi-operation-coverage norn.graph:lower:contracted:Norn `
|
||||
--expect-plugin-abi-operation-coverage norn.graph:apply:contracted:Norn `
|
||||
--expect-provider-plugin-requirement gamecult.home.vn:sai.visual_novel.surface:norn.graph:satisfied:Norn `
|
||||
--expect-provider-plugin-requirement gamecult.home.vn:sai.visual_novel.surface:tex.math:satisfied:EvePlugins `
|
||||
--expect-provider-plugin-requirement gamecult.home.vn:sai.visual_novel.surface:norn.graph:optional-satisfied:Norn `
|
||||
--expect-provider-plugin-requirement gamecult.home.vn:sai.visual_novel.surface:tex.math:optional-satisfied:EvePlugins `
|
||||
--expect-plugin-capability sai.vn:vn.stage `
|
||||
--expect-plugin-capability sai.vn:story.choose `
|
||||
--expect-plugin-capability sai.vn:story.continue `
|
||||
|
|
|
|||
|
|
@ -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-projection unity-uitoolkit:sai.vn:supported:EveUnity `
|
||||
--expect-runtime-plugin-projection unity-uitoolkit:norn.graph:supported:EveUnity `
|
||||
--expect-runtime-plugin-projection unity-uitoolkit:tex.math:unsupported:EveUnity `
|
||||
--expect-runtime-plugin-projection unity-scene:sai.vn:unsupported:EveUnity `
|
||||
--expect-runtime-plugin-projection electron-shell:sai.vn:unsupported:EveElectron `
|
||||
--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 `
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ if (!exportDirectory) {
|
|||
" --expect-split-target <id>",
|
||||
" --expect-capability-matrix",
|
||||
" --expect-capability-gap <substring>",
|
||||
" --expect-runtime-plugin-projection <runtimeId:pluginId:status:ownerRepo>",
|
||||
" --expect-runtime-plugin-gap <runtimeId:pluginId:ownerRepo>",
|
||||
" --expect-world-lowering-coverage <providerId:surfaceId:targetId:status:ownerRepo:runtimeId>",
|
||||
" --expect-command-boundary-coverage <providerId:surfaceId:targetId:status:ownerRepo:runtimeId>",
|
||||
|
|
@ -132,6 +133,7 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
const pluginAbiOperationCoverage = Array.isArray(index.pluginAbiOperationCoverage) ? index.pluginAbiOperationCoverage : [];
|
||||
const providerPluginRequirementCoverage = Array.isArray(index.providerPluginRequirementCoverage) ? index.providerPluginRequirementCoverage : [];
|
||||
const capabilityGaps = Array.isArray(index.capabilityGaps) ? index.capabilityGaps : [];
|
||||
const runtimePluginProjectionCoverage = Array.isArray(index.runtimePluginProjectionCoverage) ? index.runtimePluginProjectionCoverage : [];
|
||||
const runtimePluginProjectionGaps = Array.isArray(index.runtimePluginProjectionGaps) ? index.runtimePluginProjectionGaps : [];
|
||||
const interactiveWorldSurfaces = Array.isArray(index.interactiveWorldSurfaces) ? index.interactiveWorldSurfaces : [];
|
||||
const worldSurfaceLoweringCoverage = Array.isArray(index.worldSurfaceLoweringCoverage) ? index.worldSurfaceLoweringCoverage : [];
|
||||
|
|
@ -158,6 +160,7 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
validateCapabilityMatrix(index.capabilityMatrix, packs, plugins, providers, runtimes, splitTargets, errors);
|
||||
}
|
||||
validateCapabilityGaps(index.capabilityGaps, errors);
|
||||
validateRuntimePluginProjectionCoverage(index.runtimePluginProjectionCoverage, errors);
|
||||
validateRuntimePluginProjectionGaps(index.runtimePluginProjectionGaps, errors);
|
||||
validateSplitTargetBlockers(index.splitTargetBlockers, errors);
|
||||
validateSplitHandoffMoveCoverage(index.splitHandoffMoveCoverage, errors);
|
||||
|
|
@ -169,6 +172,21 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
errors.push(`capabilityGaps:${expectedGap}:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.runtimePluginProjectionCoverage) {
|
||||
const record = runtimePluginProjectionCoverage.find(candidate =>
|
||||
candidate.runtimeId === expectation.runtimeId &&
|
||||
candidate.pluginId === expectation.pluginId);
|
||||
if (!record) {
|
||||
errors.push(`runtimePluginProjectionCoverage:${expectation.runtimeId}:${expectation.pluginId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (record.status !== expectation.status) {
|
||||
errors.push(`runtimePluginProjectionCoverage:${expectation.runtimeId}:${expectation.pluginId}:status:expected ${expectation.status} got ${record.status || ""}`);
|
||||
}
|
||||
if (record.runtimeOwnerRepo !== expectation.ownerRepo) {
|
||||
errors.push(`runtimePluginProjectionCoverage:${expectation.runtimeId}:${expectation.pluginId}:runtimeOwnerRepo:expected ${expectation.ownerRepo} got ${record.runtimeOwnerRepo || ""}`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.runtimePluginGaps) {
|
||||
const gap = runtimePluginProjectionGaps.find(candidate =>
|
||||
candidate.runtimeId === expectation.runtimeId &&
|
||||
|
|
@ -724,6 +742,27 @@ function validateCapabilityGaps(gaps, errors) {
|
|||
}
|
||||
}
|
||||
|
||||
function validateRuntimePluginProjectionCoverage(records, errors) {
|
||||
if (!Array.isArray(records)) {
|
||||
errors.push("runtimePluginProjectionCoverage:missing");
|
||||
return;
|
||||
}
|
||||
for (const [index, record] of records.entries()) {
|
||||
for (const field of ["runtimeId", "runtimeStatus", "runtimeOwnerRepo", "pluginId", "pluginOwnerRepo", "status", "severity"]) {
|
||||
if (!record?.[field]) errors.push(`runtimePluginProjectionCoverage:${index}:${field}:missing`);
|
||||
}
|
||||
for (const field of ["capabilities", "requiredFixtures", "pluginFixtures"]) {
|
||||
if (!Array.isArray(record?.[field])) errors.push(`runtimePluginProjectionCoverage:${index}:${field}:expected array`);
|
||||
}
|
||||
if (record?.status === "supported" && !record.capabilities.length) {
|
||||
errors.push(`runtimePluginProjectionCoverage:${index}:capabilities:missing`);
|
||||
}
|
||||
if (record?.status === "unsupported" && !record.reason) {
|
||||
errors.push(`runtimePluginProjectionCoverage:${index}:reason:missing`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateRuntimePluginProjectionGaps(gaps, errors) {
|
||||
if (!Array.isArray(gaps)) {
|
||||
errors.push("runtimePluginProjectionGaps:missing");
|
||||
|
|
@ -862,10 +901,10 @@ function validateProviderPluginRequirementCoverage(records, errors) {
|
|||
return;
|
||||
}
|
||||
for (const [index, record] of records.entries()) {
|
||||
for (const field of ["providerId", "providerOwnerRepo", "surfaceId", "pluginId", "pluginStatus", "status"]) {
|
||||
for (const field of ["providerId", "providerOwnerRepo", "surfaceId", "pluginId", "pluginStatus", "availability", "status"]) {
|
||||
if (!record?.[field]) errors.push(`providerPluginRequirementCoverage:${index}:${field}:missing`);
|
||||
}
|
||||
for (const field of ["requiredCapabilities", "optionalCapabilities", "missingRequiredCapabilities"]) {
|
||||
for (const field of ["requiredCapabilities", "optionalCapabilities", "missingRequiredCapabilities", "missingOptionalCapabilities"]) {
|
||||
if (!Array.isArray(record?.[field])) errors.push(`providerPluginRequirementCoverage:${index}:${field}:expected array`);
|
||||
}
|
||||
}
|
||||
|
|
@ -965,6 +1004,7 @@ function parseArguments(args) {
|
|||
splitTargetProofs: [],
|
||||
capabilityMatrix: false,
|
||||
capabilityGaps: [],
|
||||
runtimePluginProjectionCoverage: [],
|
||||
runtimePluginGaps: [],
|
||||
worldLoweringCoverage: [],
|
||||
commandBoundaryCoverage: [],
|
||||
|
|
@ -1012,6 +1052,7 @@ function parseArguments(args) {
|
|||
["--expect-split-handoff-move", expectations.splitHandoffMoves],
|
||||
["--expect-split-target-proof", expectations.splitTargetProofs],
|
||||
["--expect-capability-gap", expectations.capabilityGaps],
|
||||
["--expect-runtime-plugin-projection", expectations.runtimePluginProjectionCoverage],
|
||||
["--expect-runtime-plugin-gap", expectations.runtimePluginGaps],
|
||||
["--expect-world-lowering-coverage", expectations.worldLoweringCoverage],
|
||||
["--expect-command-boundary-coverage", expectations.commandBoundaryCoverage],
|
||||
|
|
@ -1110,6 +1151,8 @@ function parseArguments(args) {
|
|||
target.push(parseCommandBoundaryCoverageExpectation(value));
|
||||
} else if (option === "--expect-runtime-plugin-gap") {
|
||||
target.push(parseRuntimePluginGapExpectation(value));
|
||||
} else if (option === "--expect-runtime-plugin-projection") {
|
||||
target.push(parseRuntimePluginProjectionCoverageExpectation(value));
|
||||
} else {
|
||||
target.push(value);
|
||||
}
|
||||
|
|
@ -1179,6 +1222,20 @@ function parseRuntimePluginGapExpectation(value) {
|
|||
};
|
||||
}
|
||||
|
||||
function parseRuntimePluginProjectionCoverageExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length !== 4 || parts.some(part => !part)) {
|
||||
console.error(`Expected runtime plugin projection coverage in <runtimeId:pluginId:status:ownerRepo> form, got: ${value}`);
|
||||
process.exit(2);
|
||||
}
|
||||
return {
|
||||
runtimeId: parts[0],
|
||||
pluginId: parts[1],
|
||||
status: parts[2],
|
||||
ownerRepo: parts[3],
|
||||
};
|
||||
}
|
||||
|
||||
function parseSplitTargetBlockerRecordExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length < 3 || !parts[0] || !parts[1] || !parts.slice(2).join(":")) {
|
||||
|
|
|
|||
|
|
@ -725,6 +725,7 @@ function collectProviderPluginRequirements(advertisement) {
|
|||
surfaceId: surface.surfaceId || "",
|
||||
pluginId: requirement.pluginId || "",
|
||||
versionRange: requirement.versionRange || "",
|
||||
availability: requirement.availability || "required",
|
||||
requiredCapabilities: requirement.requiredCapabilities || [],
|
||||
optionalCapabilities: requirement.optionalCapabilities || [],
|
||||
});
|
||||
|
|
@ -775,8 +776,9 @@ function validateProviderPluginRequirements(provider, requirements, pluginResult
|
|||
const pluginById = new Map((pluginResults || []).map(plugin => [plugin.pluginId, plugin]));
|
||||
for (const requirement of requirements) {
|
||||
const plugin = pluginById.get(requirement.pluginId);
|
||||
const optional = requirement.availability && requirement.availability !== "required";
|
||||
if (!plugin) {
|
||||
errors.push(`${provider.advertisementPath}:${requirement.surfaceId}:plugin:${requirement.pluginId}:missing`);
|
||||
if (!optional) errors.push(`${provider.advertisementPath}:${requirement.surfaceId}:plugin:${requirement.pluginId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (plugin.status !== "incubating" && plugin.status !== "external-owner-planned") {
|
||||
|
|
@ -1669,6 +1671,7 @@ function buildConformanceExport(report) {
|
|||
conformanceHandoffExportPath: makeHandoffExportPath("conformance", "EveConformance", report.repoStrategy.conformanceHandoffPath || ""),
|
||||
packs,
|
||||
capabilityMatrix: buildCapabilityMatrix(report),
|
||||
runtimePluginProjectionCoverage: collectRuntimePluginProjectionCoverage(report),
|
||||
runtimePluginProjectionGaps: collectRuntimePluginProjectionGaps(report),
|
||||
interactiveWorldSurfaces: collectInteractiveWorldSurfaces(report),
|
||||
worldSurfaceLoweringCoverage: collectWorldSurfaceLoweringCoverage(report),
|
||||
|
|
@ -1953,6 +1956,53 @@ function collectRuntimePluginProjectionGaps(report) {
|
|||
return gaps;
|
||||
}
|
||||
|
||||
function collectRuntimePluginProjectionCoverage(report) {
|
||||
const pluginById = new Map((report.plugins || []).map(plugin => [plugin.pluginId, plugin]));
|
||||
const coverage = [];
|
||||
for (const runtime of report.runtimes || []) {
|
||||
for (const plugin of runtime.supportedPlugins || []) {
|
||||
if (!plugin.pluginId) continue;
|
||||
const pluginRecord = pluginById.get(plugin.pluginId);
|
||||
coverage.push({
|
||||
runtimeId: runtime.id,
|
||||
runtimeStatus: runtime.status || "",
|
||||
runtimeOwnerRepo: resolveRuntimeProjectionOwnerRepo(runtime),
|
||||
splitTarget: runtime.splitTarget || "",
|
||||
pluginId: plugin.pluginId,
|
||||
pluginOwnerRepo: pluginRecord?.ownerRepo || "",
|
||||
pluginStatus: pluginRecord?.status || "",
|
||||
status: "supported",
|
||||
severity: "ok",
|
||||
capabilities: plugin.capabilities || [],
|
||||
reason: "",
|
||||
requiredFixtures: runtime.requiredFixtures || [],
|
||||
pluginFixtures: runtime.pluginFixtures || [],
|
||||
});
|
||||
}
|
||||
for (const plugin of runtime.unsupportedPlugins || []) {
|
||||
if (!plugin.pluginId) continue;
|
||||
const pluginRecord = pluginById.get(plugin.pluginId);
|
||||
coverage.push({
|
||||
runtimeId: runtime.id,
|
||||
runtimeStatus: runtime.status || "",
|
||||
runtimeOwnerRepo: resolveRuntimeProjectionOwnerRepo(runtime),
|
||||
splitTarget: runtime.splitTarget || "",
|
||||
pluginId: plugin.pluginId,
|
||||
pluginOwnerRepo: pluginRecord?.ownerRepo || "",
|
||||
pluginStatus: pluginRecord?.status || "",
|
||||
status: "unsupported",
|
||||
severity: "declared-gap",
|
||||
capabilities: [],
|
||||
reason: plugin.reason || "unsupported",
|
||||
requiredFixtures: runtime.requiredFixtures || [],
|
||||
pluginFixtures: runtime.pluginFixtures || [],
|
||||
});
|
||||
}
|
||||
}
|
||||
return coverage.sort((left, right) =>
|
||||
`${left.runtimeId}:${left.pluginId}:${left.status}`.localeCompare(`${right.runtimeId}:${right.pluginId}:${right.status}`));
|
||||
}
|
||||
|
||||
function collectPluginAbiOperationCoverage(report) {
|
||||
const requiredOperations = ["describe", "validate", "project", "lower", "measure", "apply"];
|
||||
const coverage = [];
|
||||
|
|
@ -1992,6 +2042,9 @@ function collectProviderPluginRequirementCoverage(report) {
|
|||
const pluginCapabilities = new Set(plugin?.capabilities || []);
|
||||
const missingRequiredCapabilities = (requirement.requiredCapabilities || [])
|
||||
.filter(capability => !pluginCapabilities.has(capability));
|
||||
const missingOptionalCapabilities = (requirement.optionalCapabilities || [])
|
||||
.filter(capability => plugin && !pluginCapabilities.has(capability));
|
||||
const optional = requirement.availability && requirement.availability !== "required";
|
||||
coverage.push({
|
||||
providerId: provider.providerId,
|
||||
providerOwnerRepo: provider.ownerRepo || "",
|
||||
|
|
@ -2000,14 +2053,16 @@ function collectProviderPluginRequirementCoverage(report) {
|
|||
pluginOwnerRepo: plugin?.ownerRepo || "",
|
||||
pluginStatus: plugin?.status || "missing",
|
||||
versionRange: requirement.versionRange || "",
|
||||
availability: requirement.availability || "required",
|
||||
status: !plugin
|
||||
? "missing-plugin"
|
||||
? optional ? "optional-missing-plugin" : "missing-plugin"
|
||||
: missingRequiredCapabilities.length
|
||||
? "missing-capability"
|
||||
: "satisfied",
|
||||
? optional ? "optional-missing-capability" : "missing-capability"
|
||||
: optional ? "optional-satisfied" : "satisfied",
|
||||
requiredCapabilities: requirement.requiredCapabilities || [],
|
||||
optionalCapabilities: requirement.optionalCapabilities || [],
|
||||
missingRequiredCapabilities,
|
||||
missingOptionalCapabilities,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,11 +44,12 @@
|
|||
"status": "available",
|
||||
"audience": "player",
|
||||
"mode": "interactive",
|
||||
"summary": "Visual novel surface requiring sidecar plugin capabilities for VN stage, Norn graph embedding, and TeX embedding.",
|
||||
"summary": "Visual novel surface requiring Sai VN semantics, with optional independently-owned Norn graph and TeX nested surfaces when those plugins are available.",
|
||||
"requiresPlugins": [
|
||||
{
|
||||
"pluginId": "sai.vn",
|
||||
"versionRange": "^0.1.0",
|
||||
"availability": "required",
|
||||
"requiredCapabilities": [
|
||||
"vn.stage",
|
||||
"story.choose",
|
||||
|
|
@ -59,14 +60,18 @@
|
|||
{
|
||||
"pluginId": "norn.graph",
|
||||
"versionRange": "^0.1.0",
|
||||
"requiredCapabilities": [
|
||||
"availability": "optional-nested",
|
||||
"requiredCapabilities": [],
|
||||
"optionalCapabilities": [
|
||||
"embed.norn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pluginId": "tex.math",
|
||||
"versionRange": "^0.1.0",
|
||||
"requiredCapabilities": [
|
||||
"availability": "optional-nested",
|
||||
"requiredCapabilities": [],
|
||||
"optionalCapabilities": [
|
||||
"embed.tex"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue