Declare plugin sidecar ABI boundaries
This commit is contained in:
parent
0381da7463
commit
169dbfafd4
16 changed files with 310 additions and 4 deletions
|
|
@ -280,6 +280,13 @@ Exit criteria:
|
|||
|
||||
Recently cut:
|
||||
|
||||
- Plugin manifests and advertisements now carry a runtime-independent sidecar
|
||||
ABI boundary. Sai, Norn, and TeX declare `invocationModel:
|
||||
executable-sidecar`, `gamecult.eve.plugin_abi.v1`, CultMesh/stdio transports,
|
||||
renderer independence, no provider-state mutation, and provider-owned command
|
||||
acceptance. The parity harness validates manifest/advertisement agreement,
|
||||
exports the runtime boundary, and the plugin-owner consumer smoke asserts the
|
||||
Sai/Norn sidecar contract from the conformance pack.
|
||||
- Conformance export now carries first-class `capabilityGaps[]` records derived
|
||||
from plugin errors, provider advertisement/scenario errors, runtime missing
|
||||
evidence, declared unsupported plugin projection, capture gaps, and split
|
||||
|
|
|
|||
|
|
@ -156,8 +156,12 @@ powershell -ExecutionPolicy Bypass -File .\scripts\run-plugin-handoff-smoke.ps1
|
|||
|
||||
That script validates the Sai and Norn handoff manifests against their plugin
|
||||
manifests, advertisements, ABI fixtures, move-set paths, contract inputs, and
|
||||
external proofs. The plugin-owner conformance smoke also asserts those handoff
|
||||
paths from the conformance export.
|
||||
external proofs. It also checks that the manifest and advertisement agree on
|
||||
the runtime-independent executable sidecar boundary: `gamecult.eve.plugin_abi.v1`,
|
||||
CultMesh/stdio transport, renderer independence, no provider-state mutation,
|
||||
and provider-owned command acceptance. The plugin-owner conformance smoke also
|
||||
asserts those handoff paths and runtime boundary claims from the conformance
|
||||
export.
|
||||
|
||||
Aetheria provider handoff smoke:
|
||||
|
||||
|
|
|
|||
|
|
@ -114,6 +114,14 @@ not by importing plugin internals directly. A plugin implementation may use a
|
|||
same-language library internally, but that is an implementation detail behind
|
||||
the executable plugin boundary.
|
||||
|
||||
Plugin manifests and advertisements declare a `runtime` block. Its normal
|
||||
incubating shape is `invocationModel: executable-sidecar`,
|
||||
`contract: gamecult.eve.plugin_abi.v1`, CultMesh plus stdio transports, and
|
||||
authority limits such as `renderer-independent`, `no-provider-state-mutation`,
|
||||
and `provider-accepts-or-denies-commands`. Runtimes consume that advertisement
|
||||
as a capability boundary. A Unity or web projection adapter may render an
|
||||
advertised capability; it does not become the plugin runtime.
|
||||
|
||||
The ownership rule is semantic, not packaging ceremony: the plugin owns the
|
||||
capability and representation shape, while providers own live state and
|
||||
renderer clients own projection.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,16 @@
|
|||
"ownerService": "norn",
|
||||
"version": "0.1.0-incubating",
|
||||
"manifestAddress": "file://plugins/incubating/norn-graph.plugin.json",
|
||||
"runtime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": [
|
||||
"renderer-independent",
|
||||
"no-provider-state-mutation",
|
||||
"provider-accepts-or-denies-commands"
|
||||
]
|
||||
},
|
||||
"schemas": [
|
||||
"norn.graph.document.v1",
|
||||
"norn.graph.layout_request.v1",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,16 @@
|
|||
"effect": "focus-graph"
|
||||
}
|
||||
],
|
||||
"runtime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": [
|
||||
"renderer-independent",
|
||||
"no-provider-state-mutation",
|
||||
"provider-accepts-or-denies-commands"
|
||||
]
|
||||
},
|
||||
"stateSchemas": [
|
||||
"norn.graph.document.v1",
|
||||
"norn.graph.layout_request.v1",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,16 @@
|
|||
"ownerService": "sai",
|
||||
"version": "0.1.0-incubating",
|
||||
"manifestAddress": "file://plugins/incubating/sai-vn.plugin.json",
|
||||
"runtime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": [
|
||||
"renderer-independent",
|
||||
"no-provider-state-mutation",
|
||||
"provider-accepts-or-denies-commands"
|
||||
]
|
||||
},
|
||||
"schemas": [
|
||||
"sai.vn.story_session.v1",
|
||||
"sai.vn.visual_manifest.v1"
|
||||
|
|
|
|||
|
|
@ -32,6 +32,16 @@
|
|||
"effect": "jump-to-knot"
|
||||
}
|
||||
],
|
||||
"runtime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": [
|
||||
"renderer-independent",
|
||||
"no-provider-state-mutation",
|
||||
"provider-accepts-or-denies-commands"
|
||||
]
|
||||
},
|
||||
"stateSchemas": [
|
||||
"sai.vn.story_session.v1",
|
||||
"sai.vn.visual_manifest.v1"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,16 @@
|
|||
"ownerService": "eveplugins",
|
||||
"version": "0.1.0-incubating",
|
||||
"manifestAddress": "file://plugins/incubating/tex-math.plugin.json",
|
||||
"runtime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": [
|
||||
"renderer-independent",
|
||||
"no-provider-state-mutation",
|
||||
"provider-accepts-or-denies-commands"
|
||||
]
|
||||
},
|
||||
"schemas": [
|
||||
"tex.math.source.v1",
|
||||
"tex.math.render_request.v1",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,16 @@
|
|||
"embed.tex"
|
||||
],
|
||||
"commands": [],
|
||||
"runtime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": [
|
||||
"renderer-independent",
|
||||
"no-provider-state-mutation",
|
||||
"provider-accepts-or-denies-commands"
|
||||
]
|
||||
},
|
||||
"stateSchemas": [
|
||||
"tex.math.source.v1",
|
||||
"tex.math.render_request.v1",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"incubation",
|
||||
"componentKinds",
|
||||
"commands",
|
||||
"runtime",
|
||||
"stateSchemas",
|
||||
"fixtures",
|
||||
"capabilities"
|
||||
|
|
@ -92,6 +93,40 @@
|
|||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"runtime": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"invocationModel",
|
||||
"contract",
|
||||
"transports",
|
||||
"authority"
|
||||
],
|
||||
"properties": {
|
||||
"invocationModel": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"contract": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"transports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"authority": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"stateSchemas": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
"ownerService",
|
||||
"version",
|
||||
"manifestAddress",
|
||||
"runtime",
|
||||
"schemas",
|
||||
"componentKinds",
|
||||
"commands",
|
||||
|
|
@ -34,6 +35,40 @@
|
|||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"runtime": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"invocationModel",
|
||||
"contract",
|
||||
"transports",
|
||||
"authority"
|
||||
],
|
||||
"properties": {
|
||||
"invocationModel": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"contract": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"transports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"authority": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"schemas": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
|
|||
|
|
@ -65,6 +65,31 @@ foreach ($handoffPath in $HandoffPaths) {
|
|||
if ($manifest.incubation.splitTarget -ne $handoff.splitTarget) {
|
||||
throw "Plugin handoff $($handoff.pluginId) split target does not match manifest: $($manifest.incubation.splitTarget)"
|
||||
}
|
||||
if ($manifest.runtime.invocationModel -ne "executable-sidecar") {
|
||||
throw "Plugin handoff $($handoff.pluginId) runtime is not executable-sidecar: $($manifest.runtime.invocationModel)"
|
||||
}
|
||||
if ($manifest.runtime.contract -ne "gamecult.eve.plugin_abi.v1") {
|
||||
throw "Plugin handoff $($handoff.pluginId) runtime contract is not gamecult.eve.plugin_abi.v1: $($manifest.runtime.contract)"
|
||||
}
|
||||
if ($advertisement.runtime.invocationModel -ne $manifest.runtime.invocationModel) {
|
||||
throw "Plugin handoff $($handoff.pluginId) advertisement runtime does not match manifest runtime: $($advertisement.runtime.invocationModel)"
|
||||
}
|
||||
foreach ($transport in @("cultmesh", "stdio")) {
|
||||
if (-not (@($manifest.runtime.transports) -contains $transport)) {
|
||||
throw "Plugin handoff $($handoff.pluginId) manifest runtime missing transport: $transport"
|
||||
}
|
||||
if (-not (@($advertisement.runtime.transports) -contains $transport)) {
|
||||
throw "Plugin handoff $($handoff.pluginId) advertisement runtime missing transport: $transport"
|
||||
}
|
||||
}
|
||||
foreach ($authority in @("renderer-independent", "no-provider-state-mutation", "provider-accepts-or-denies-commands")) {
|
||||
if (-not (@($manifest.runtime.authority) -contains $authority)) {
|
||||
throw "Plugin handoff $($handoff.pluginId) manifest runtime missing authority: $authority"
|
||||
}
|
||||
if (-not (@($advertisement.runtime.authority) -contains $authority)) {
|
||||
throw "Plugin handoff $($handoff.pluginId) advertisement runtime missing authority: $authority"
|
||||
}
|
||||
}
|
||||
|
||||
$operations = @($abiFixture.operations | ForEach-Object { $_.operation })
|
||||
foreach ($operation in @("describe", "validate", "project", "lower", "measure", "apply")) {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,13 @@ node $consumerScript $consumerExport `
|
|||
--expect-plugin-capability sai.vn:story.continue `
|
||||
--expect-plugin-capability sai.vn:story.jump `
|
||||
--expect-plugin-capability norn.graph:embed.norn `
|
||||
--expect-plugin-capability norn.graph:graph.node.activate
|
||||
--expect-plugin-capability norn.graph:graph.node.activate `
|
||||
--expect-plugin-runtime sai.vn:executable-sidecar `
|
||||
--expect-plugin-runtime-transport sai.vn:cultmesh `
|
||||
--expect-plugin-runtime-authority sai.vn:no-provider-state-mutation `
|
||||
--expect-plugin-runtime norn.graph:executable-sidecar `
|
||||
--expect-plugin-runtime-transport norn.graph:cultmesh `
|
||||
--expect-plugin-runtime-authority norn.graph:no-provider-state-mutation
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Plugin owner conformance consumer smoke failed with exit code $LASTEXITCODE"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ if (!exportDirectory) {
|
|||
" --expect-conformance-handoff",
|
||||
" --expect-plugin-operation <pluginId:operation>",
|
||||
" --expect-plugin-capability <pluginId:capability>",
|
||||
" --expect-plugin-runtime <pluginId:invocationModel>",
|
||||
" --expect-plugin-runtime-transport <pluginId:transport>",
|
||||
" --expect-plugin-runtime-authority <pluginId:authority>",
|
||||
" --expect-plugin-handoff <pluginId>",
|
||||
" --expect-provider-surface <providerId:surfaceId>",
|
||||
" --expect-provider-surface-kind <providerId:surfaceId:surfaceKind>",
|
||||
|
|
@ -153,6 +156,36 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
errors.push(`plugins:${expectation.pluginId}:capability:${expectation.capability}:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.pluginRuntimes) {
|
||||
const plugin = plugins.find(candidate => candidate.pluginId === expectation.pluginId);
|
||||
if (!plugin) {
|
||||
errors.push(`plugins:${expectation.pluginId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (plugin.runtimeBoundary?.invocationModel !== expectation.invocationModel) {
|
||||
errors.push(`plugins:${expectation.pluginId}:runtime:expected ${expectation.invocationModel} got ${plugin.runtimeBoundary?.invocationModel || ""}`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.pluginRuntimeTransports) {
|
||||
const plugin = plugins.find(candidate => candidate.pluginId === expectation.pluginId);
|
||||
if (!plugin) {
|
||||
errors.push(`plugins:${expectation.pluginId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (!Array.isArray(plugin.runtimeBoundary?.transports) || !plugin.runtimeBoundary.transports.includes(expectation.transport)) {
|
||||
errors.push(`plugins:${expectation.pluginId}:runtime.transport:${expectation.transport}:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.pluginRuntimeAuthorities) {
|
||||
const plugin = plugins.find(candidate => candidate.pluginId === expectation.pluginId);
|
||||
if (!plugin) {
|
||||
errors.push(`plugins:${expectation.pluginId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (!Array.isArray(plugin.runtimeBoundary?.authority) || !plugin.runtimeBoundary.authority.includes(expectation.authority)) {
|
||||
errors.push(`plugins:${expectation.pluginId}:runtime.authority:${expectation.authority}:missing`);
|
||||
}
|
||||
}
|
||||
for (const expectedPlugin of expectations.pluginHandoffs) {
|
||||
const plugin = plugins.find(candidate => candidate.pluginId === expectedPlugin);
|
||||
if (!plugin) {
|
||||
|
|
@ -422,6 +455,9 @@ function parseArguments(args) {
|
|||
plugins: [],
|
||||
pluginOperations: [],
|
||||
pluginCapabilities: [],
|
||||
pluginRuntimes: [],
|
||||
pluginRuntimeTransports: [],
|
||||
pluginRuntimeAuthorities: [],
|
||||
pluginHandoffs: [],
|
||||
providers: [],
|
||||
providerSurfaces: [],
|
||||
|
|
@ -454,6 +490,9 @@ function parseArguments(args) {
|
|||
["--expect-split-target", expectations.splitTargets],
|
||||
["--expect-plugin-operation", expectations.pluginOperations],
|
||||
["--expect-plugin-capability", expectations.pluginCapabilities],
|
||||
["--expect-plugin-runtime", expectations.pluginRuntimes],
|
||||
["--expect-plugin-runtime-transport", expectations.pluginRuntimeTransports],
|
||||
["--expect-plugin-runtime-authority", expectations.pluginRuntimeAuthorities],
|
||||
["--expect-plugin-handoff", expectations.pluginHandoffs],
|
||||
["--expect-provider-surface", expectations.providerSurfaces],
|
||||
["--expect-provider-surface-kind", expectations.providerSurfaceKinds],
|
||||
|
|
@ -495,6 +534,12 @@ function parseArguments(args) {
|
|||
target.push(parsePluginExpectation(value, "operation"));
|
||||
} else if (option === "--expect-plugin-capability") {
|
||||
target.push(parsePluginExpectation(value, "capability"));
|
||||
} else if (option === "--expect-plugin-runtime") {
|
||||
target.push(parsePluginExpectation(value, "invocationModel"));
|
||||
} else if (option === "--expect-plugin-runtime-transport") {
|
||||
target.push(parsePluginExpectation(value, "transport"));
|
||||
} else if (option === "--expect-plugin-runtime-authority") {
|
||||
target.push(parsePluginExpectation(value, "authority"));
|
||||
} else if (option === "--expect-plugin-handoff") {
|
||||
target.push(value);
|
||||
} else if (option === "--expect-provider-surface") {
|
||||
|
|
|
|||
|
|
@ -372,6 +372,12 @@
|
|||
"graduationTrigger": "Split when VN/Ink semantics, story command fixtures, and ABI project/apply/validate cases are stable.",
|
||||
"capabilities": ["vn.stage", "story.choose", "story.continue", "story.jump"],
|
||||
"optionalPlugins": ["norn.graph", "tex.math"],
|
||||
"expectedRuntime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": ["renderer-independent", "no-provider-state-mutation", "provider-accepts-or-denies-commands"]
|
||||
},
|
||||
"requiredFixtures": ["sai-vn"],
|
||||
"schemaPath": "schemas/gamecult.eve.plugin.v1.schema.json",
|
||||
"manifestPath": "plugins/incubating/sai-vn.plugin.json",
|
||||
|
|
@ -396,6 +402,12 @@
|
|||
"splitTarget": "Norn/eve-plugin-graph",
|
||||
"graduationTrigger": "Split when embed.norn graph layout, interaction, fallback, and conformance fixtures are stable.",
|
||||
"capabilities": ["embed.norn", "graph.node.activate"],
|
||||
"expectedRuntime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": ["renderer-independent", "no-provider-state-mutation", "provider-accepts-or-denies-commands"]
|
||||
},
|
||||
"requiredFixtures": ["sai-vn"],
|
||||
"schemaPath": "schemas/gamecult.eve.plugin.v1.schema.json",
|
||||
"manifestPath": "plugins/incubating/norn-graph.plugin.json",
|
||||
|
|
@ -420,6 +432,12 @@
|
|||
"splitTarget": "EvePlugins/eve-plugin-tex",
|
||||
"graduationTrigger": "Split when TeX source, macro, baseline, cached render, and fallback fixtures are stable.",
|
||||
"capabilities": ["embed.tex", "tex.inline", "tex.block"],
|
||||
"expectedRuntime": {
|
||||
"invocationModel": "executable-sidecar",
|
||||
"contract": "gamecult.eve.plugin_abi.v1",
|
||||
"transports": ["cultmesh", "stdio"],
|
||||
"authority": ["renderer-independent", "no-provider-state-mutation", "provider-accepts-or-denies-commands"]
|
||||
},
|
||||
"requiredFixtures": ["sai-vn"],
|
||||
"schemaPath": "schemas/gamecult.eve.plugin.v1.schema.json",
|
||||
"manifestPath": "plugins/incubating/tex-math.plugin.json",
|
||||
|
|
|
|||
|
|
@ -443,6 +443,8 @@ async function evaluatePlugin(plugin, fixtureResults) {
|
|||
schema: "gamecult.eve.plugin_advertisement.v1",
|
||||
pluginId: plugin.pluginId,
|
||||
});
|
||||
const runtimeBoundary = await readPluginRuntimeBoundary(plugin);
|
||||
const runtimeBoundaryErrors = validatePluginRuntimeBoundary(plugin, runtimeBoundary);
|
||||
const abiOperations = await readPluginAbiOperations(plugin.abiFixturePath);
|
||||
const abiErrors = await validatePluginAbiFixture(plugin);
|
||||
const status = missingPaths.length
|
||||
|
|
@ -455,7 +457,9 @@ async function evaluatePlugin(plugin, fixtureResults) {
|
|||
? "invalid-plugin-manifest"
|
||||
: advertisementErrors.length
|
||||
? "invalid-plugin-advertisement"
|
||||
: abiErrors.length
|
||||
: runtimeBoundaryErrors.length
|
||||
? "invalid-plugin-runtime-boundary"
|
||||
: abiErrors.length
|
||||
? "invalid-plugin-abi-fixture"
|
||||
: plugin.kind === "incubating"
|
||||
? "incubating"
|
||||
|
|
@ -471,6 +475,8 @@ async function evaluatePlugin(plugin, fixtureResults) {
|
|||
splitTarget: plugin.splitTarget || "",
|
||||
capabilities: plugin.capabilities || [],
|
||||
optionalPlugins: plugin.optionalPlugins || [],
|
||||
runtimeBoundary,
|
||||
runtimeBoundaryErrors,
|
||||
requiredFixtures,
|
||||
missingRequiredFixtures,
|
||||
schemaPath: plugin.schemaPath || "",
|
||||
|
|
@ -503,6 +509,60 @@ async function readPluginAbiOperations(abiFixturePath) {
|
|||
}
|
||||
}
|
||||
|
||||
async function readPluginRuntimeBoundary(plugin) {
|
||||
try {
|
||||
const manifestDocument = await readJsonDocument(plugin.manifestPath);
|
||||
const advertisementDocument = await readJsonDocument(plugin.advertisementPath);
|
||||
return {
|
||||
invocationModel: manifestDocument.runtime?.invocationModel || "",
|
||||
contract: manifestDocument.runtime?.contract || "",
|
||||
transports: manifestDocument.runtime?.transports || [],
|
||||
authority: manifestDocument.runtime?.authority || [],
|
||||
advertisementInvocationModel: advertisementDocument.runtime?.invocationModel || "",
|
||||
advertisementContract: advertisementDocument.runtime?.contract || "",
|
||||
advertisementTransports: advertisementDocument.runtime?.transports || [],
|
||||
advertisementAuthority: advertisementDocument.runtime?.authority || [],
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
invocationModel: "",
|
||||
contract: "",
|
||||
transports: [],
|
||||
authority: [],
|
||||
advertisementInvocationModel: "",
|
||||
advertisementContract: "",
|
||||
advertisementTransports: [],
|
||||
advertisementAuthority: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function validatePluginRuntimeBoundary(plugin, runtimeBoundary) {
|
||||
const errors = [];
|
||||
const expectedRuntime = plugin.expectedRuntime || {};
|
||||
if (!runtimeBoundary.invocationModel) errors.push("runtime.invocationModel:missing");
|
||||
if (!runtimeBoundary.contract) errors.push("runtime.contract:missing");
|
||||
if (!runtimeBoundary.transports?.length) errors.push("runtime.transports:missing");
|
||||
if (!runtimeBoundary.authority?.length) errors.push("runtime.authority:missing");
|
||||
if (runtimeBoundary.invocationModel !== runtimeBoundary.advertisementInvocationModel) {
|
||||
errors.push(`runtime.invocationModel:advertisement:expected ${runtimeBoundary.invocationModel} got ${runtimeBoundary.advertisementInvocationModel}`);
|
||||
}
|
||||
if (runtimeBoundary.contract !== runtimeBoundary.advertisementContract) {
|
||||
errors.push(`runtime.contract:advertisement:expected ${runtimeBoundary.contract} got ${runtimeBoundary.advertisementContract}`);
|
||||
}
|
||||
if (expectedRuntime.invocationModel && runtimeBoundary.invocationModel !== expectedRuntime.invocationModel) {
|
||||
errors.push(`runtime.invocationModel:expected ${expectedRuntime.invocationModel} got ${runtimeBoundary.invocationModel}`);
|
||||
}
|
||||
if (expectedRuntime.contract && runtimeBoundary.contract !== expectedRuntime.contract) {
|
||||
errors.push(`runtime.contract:expected ${expectedRuntime.contract} got ${runtimeBoundary.contract}`);
|
||||
}
|
||||
errors.push(...missingMembers(expectedRuntime.transports || [], runtimeBoundary.transports || [], "runtime.transports"));
|
||||
errors.push(...missingMembers(expectedRuntime.transports || [], runtimeBoundary.advertisementTransports || [], "runtime.advertisementTransports"));
|
||||
errors.push(...missingMembers(expectedRuntime.authority || [], runtimeBoundary.authority || [], "runtime.authority"));
|
||||
errors.push(...missingMembers(expectedRuntime.authority || [], runtimeBoundary.advertisementAuthority || [], "runtime.advertisementAuthority"));
|
||||
return errors;
|
||||
}
|
||||
|
||||
async function evaluateFixtureMetadata(fixture) {
|
||||
const errors = [];
|
||||
if (!fixture.metadataPath) return { metadata: null, errors: ["metadataPath:missing"] };
|
||||
|
|
@ -1399,6 +1459,7 @@ function buildConformanceExport(report) {
|
|||
handoffExportPath: makeHandoffExportPath("plugin", plugin.pluginId, plugin.handoffPath),
|
||||
abiFixturePath: plugin.abiFixturePath,
|
||||
abiOperations: plugin.abiOperations || [],
|
||||
runtimeBoundary: plugin.runtimeBoundary,
|
||||
optionalPlugins: plugin.optionalPlugins || [],
|
||||
capabilities: plugin.capabilities,
|
||||
})),
|
||||
|
|
@ -1531,6 +1592,7 @@ function collectCapabilityGaps(report) {
|
|||
...(plugin.missingIncubationFields || []).map(id => `metadata:${id}`),
|
||||
...(plugin.schemaErrors || []).map(id => `schema:${id}`),
|
||||
...(plugin.advertisementErrors || []).map(id => `advertisement:${id}`),
|
||||
...(plugin.runtimeBoundaryErrors || []).map(id => `runtime-boundary:${id}`),
|
||||
...(plugin.abiErrors || []).map(id => `abi:${id}`),
|
||||
]) {
|
||||
addGap({
|
||||
|
|
@ -1793,6 +1855,7 @@ function renderMarkdown(report) {
|
|||
...plugin.missingIncubationFields.map(id => `metadata:${id}`),
|
||||
...plugin.schemaErrors.map(id => `schema:${id}`),
|
||||
...plugin.advertisementErrors.map(id => `advertisement:${id}`),
|
||||
...plugin.runtimeBoundaryErrors.map(id => `runtime-boundary:${id}`),
|
||||
...plugin.abiErrors.map(id => `abi:${id}`),
|
||||
].join(", ");
|
||||
lines.push(`| ${plugin.title || plugin.pluginId} | ${plugin.status} | ${plugin.ownerRepo} | ${(plugin.abiOperations || []).join(", ")} | ${plugin.abiFixturePath || ""} | ${plugin.capabilities.join(", ")} | ${missing} |`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue