Add plugin ABI wire schemas

This commit is contained in:
Meta Vens 2026-07-09 11:33:42 +01:00
commit 227c6bd59d
14 changed files with 147 additions and 4 deletions

View file

@ -819,6 +819,12 @@ Recently cut:
project, lower, measure, and apply behavior. The parity harness validates
those fixtures against plugin manifests, so Eve proves the ABI shape without
owning plugin semantics.
- Plugin sidecar request/response schema names are now backed by Eve contract
documents. `gamecult.eve.plugin_abi.request.v1` and
`gamecult.eve.plugin_abi.response.v1` live under `schemas/`; Sai, Norn, and
TeX ABI fixtures must name those same schemas, and parity fails if a sidecar
advertises a request/response schema that is missing from the contract
catalog.
- Split-readiness reporting now exists for `EveFlutter` and `EveUnity`. The
parity report lists member runtime health, required plugin capability claims,
pending proofs, and blockers before either repo can graduate out of Eve

View file

@ -171,10 +171,13 @@ assets. It cannot decide whether a provider command is allowed, cannot persist
app truth, and cannot write receipts except for its own runtime diagnostics.
The incubating sidecar contract is checked in three places: the plugin manifest,
the plugin advertisement, and the ABI fixture. The parity harness compares
manifest and advertisement `runtime.sidecar` fields, verifies that sidecar
operations cover the ABI fixture operations, and exports the sidecar boundary
through the conformance pack for Sai, Norn, and EvePlugins to consume.
the plugin advertisement, and the ABI fixture. The manifest and advertisement
name `gamecult.eve.plugin_abi.request.v1` and
`gamecult.eve.plugin_abi.response.v1`; the ABI fixture must name the same wire
schemas. The parity harness compares manifest and advertisement
`runtime.sidecar` fields, verifies that sidecar operations cover the ABI fixture
operations, and exports the sidecar boundary through the conformance pack for
Sai, Norn, and EvePlugins to consume.
Example ABI request:

View file

@ -4,6 +4,8 @@
"pluginId": "norn.graph",
"ownerRepo": "Norn",
"contract": "gamecult.eve.plugin_abi.v1",
"requestSchema": "gamecult.eve.plugin_abi.request.v1",
"responseSchema": "gamecult.eve.plugin_abi.response.v1",
"sourceFixture": "sai-vn",
"sourceFixtureRole": "optional-nested-placement-evidence",
"authority": "Norn owns graph semantics independently; the Sai fixture only proves a provider surface can nest embed.norn when norn.graph is available.",

View file

@ -33,6 +33,8 @@
"gamecult.eve.plugin.v1",
"gamecult.eve.plugin_advertisement.v1",
"gamecult.eve.plugin_abi.v1",
"gamecult.eve.plugin_abi.request.v1",
"gamecult.eve.plugin_abi.response.v1",
"gamecult.eve.plugin_abi_fixture.v1",
"gamecult.eve.conformance_export.v1"
],

View file

@ -4,6 +4,8 @@
"pluginId": "sai.vn",
"ownerRepo": "Sai",
"contract": "gamecult.eve.plugin_abi.v1",
"requestSchema": "gamecult.eve.plugin_abi.request.v1",
"responseSchema": "gamecult.eve.plugin_abi.response.v1",
"sourceFixture": "sai-vn",
"operations": [
{

View file

@ -33,6 +33,8 @@
"gamecult.eve.plugin.v1",
"gamecult.eve.plugin_advertisement.v1",
"gamecult.eve.plugin_abi.v1",
"gamecult.eve.plugin_abi.request.v1",
"gamecult.eve.plugin_abi.response.v1",
"gamecult.eve.plugin_abi_fixture.v1",
"gamecult.eve.conformance_export.v1"
],

View file

@ -4,6 +4,8 @@
"pluginId": "tex.math",
"ownerRepo": "EvePlugins",
"contract": "gamecult.eve.plugin_abi.v1",
"requestSchema": "gamecult.eve.plugin_abi.request.v1",
"responseSchema": "gamecult.eve.plugin_abi.response.v1",
"sourceFixture": "sai-vn",
"sourceFixtureRole": "optional-nested-placement-evidence",
"authority": "EvePlugins owns TeX semantics independently; the Sai fixture only proves a provider surface can nest embed.tex when tex.math is available.",

View file

@ -33,6 +33,8 @@
"gamecult.eve.plugin.v1",
"gamecult.eve.plugin_advertisement.v1",
"gamecult.eve.plugin_abi.v1",
"gamecult.eve.plugin_abi.request.v1",
"gamecult.eve.plugin_abi.response.v1",
"gamecult.eve.plugin_abi_fixture.v1",
"gamecult.eve.conformance_export.v1"
],

View file

@ -0,0 +1,41 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "gamecult.eve.plugin_abi.request.v1",
"title": "Eve Plugin ABI Request",
"type": "object",
"required": [
"schema",
"pluginId",
"operation",
"requestId",
"input"
],
"properties": {
"schema": {
"const": "gamecult.eve.plugin_abi.request.v1"
},
"pluginId": {
"type": "string",
"minLength": 1
},
"operation": {
"type": "string",
"minLength": 1
},
"requestId": {
"type": "string",
"minLength": 1
},
"input": {
"type": "object"
},
"context": {
"type": "object"
},
"targetRuntime": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": true
}

View file

@ -0,0 +1,45 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "gamecult.eve.plugin_abi.response.v1",
"title": "Eve Plugin ABI Response",
"type": "object",
"required": [
"schema",
"pluginId",
"operation",
"requestId",
"status",
"output"
],
"properties": {
"schema": {
"const": "gamecult.eve.plugin_abi.response.v1"
},
"pluginId": {
"type": "string",
"minLength": 1
},
"operation": {
"type": "string",
"minLength": 1
},
"requestId": {
"type": "string",
"minLength": 1
},
"status": {
"type": "string",
"minLength": 1
},
"output": {
"type": "object"
},
"diagnostics": {
"type": "array",
"items": {
"type": "object"
}
}
},
"additionalProperties": true
}

View file

@ -9,6 +9,8 @@
"pluginId",
"ownerRepo",
"contract",
"requestSchema",
"responseSchema",
"operations"
],
"properties": {
@ -31,6 +33,14 @@
"type": "string",
"minLength": 1
},
"requestSchema": {
"type": "string",
"minLength": 1
},
"responseSchema": {
"type": "string",
"minLength": 1
},
"sourceFixture": {
"type": "string",
"minLength": 1

View file

@ -101,6 +101,12 @@ foreach ($handoffPath in $HandoffPaths) {
}
$operations = @($abiFixture.operations | ForEach-Object { $_.operation })
if ($abiFixture.requestSchema -ne $manifest.runtime.sidecar.requestSchema) {
throw "Plugin handoff $($handoff.pluginId) ABI fixture request schema does not match sidecar: $($abiFixture.requestSchema)"
}
if ($abiFixture.responseSchema -ne $manifest.runtime.sidecar.responseSchema) {
throw "Plugin handoff $($handoff.pluginId) ABI fixture response schema does not match sidecar: $($abiFixture.responseSchema)"
}
foreach ($operation in @("describe", "validate", "project", "lower", "measure", "apply")) {
if (-not ($operations -contains $operation)) {
throw "Plugin handoff $($handoff.pluginId) ABI fixture missing operation: $operation"
@ -132,6 +138,8 @@ foreach ($handoffPath in $HandoffPaths) {
"gamecult.eve.plugin.v1",
"gamecult.eve.plugin_advertisement.v1",
"gamecult.eve.plugin_abi.v1",
"gamecult.eve.plugin_abi.request.v1",
"gamecult.eve.plugin_abi.response.v1",
"gamecult.eve.plugin_abi_fixture.v1",
"gamecult.eve.conformance_export.v1"
)) {

View file

@ -11,6 +11,8 @@
"gamecult.eve.conformance_export.v1": "schemas/gamecult.eve.conformance_export.v1.schema.json",
"gamecult.eve.command.v1": "schemas/gamecult.eve.command.v1.schema.json",
"gamecult.eve.plugin.v1": "schemas/gamecult.eve.plugin.v1.schema.json",
"gamecult.eve.plugin_abi.request.v1": "schemas/gamecult.eve.plugin_abi.request.v1.schema.json",
"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.runtime_capability.v1": "schemas/gamecult.eve.runtime_capability.v1.schema.json",

View file

@ -604,6 +604,11 @@ function validatePluginRuntimeBoundary(plugin, runtimeBoundary) {
errors.push(...missingMembers(expectedRuntime.authority || [], runtimeBoundary.advertisementAuthority || [], "runtime.advertisementAuthority"));
errors.push(...comparePluginSidecar(expectedRuntime.sidecar || {}, runtimeBoundary.sidecar || {}, "runtime.sidecar"));
errors.push(...comparePluginSidecar(runtimeBoundary.sidecar || {}, runtimeBoundary.advertisementSidecar || {}, "runtime.advertisementSidecar"));
for (const schemaId of [runtimeBoundary.sidecar?.requestSchema, runtimeBoundary.sidecar?.responseSchema].filter(Boolean)) {
if (!manifest.schemas?.[schemaId]) {
errors.push(`runtime.sidecar.schemaCatalog:${schemaId}:missing`);
}
}
return errors;
}
@ -926,6 +931,17 @@ async function validatePluginAbiFixture(plugin) {
if (abiFixture.contract !== "gamecult.eve.plugin_abi.v1") {
errors.push(`${plugin.abiFixturePath}:contract:expected gamecult.eve.plugin_abi.v1 got ${abiFixture.contract}`);
}
if (abiFixture.requestSchema !== pluginManifest.runtime?.sidecar?.requestSchema) {
errors.push(`${plugin.abiFixturePath}:requestSchema:expected ${pluginManifest.runtime?.sidecar?.requestSchema || ""} got ${abiFixture.requestSchema || ""}`);
}
if (abiFixture.responseSchema !== pluginManifest.runtime?.sidecar?.responseSchema) {
errors.push(`${plugin.abiFixturePath}:responseSchema:expected ${pluginManifest.runtime?.sidecar?.responseSchema || ""} got ${abiFixture.responseSchema || ""}`);
}
for (const schemaId of [abiFixture.requestSchema, abiFixture.responseSchema].filter(Boolean)) {
if (!manifest.schemas?.[schemaId]) {
errors.push(`${plugin.abiFixturePath}:schemaCatalog:${schemaId}:missing`);
}
}
for (const operation of ["describe", "validate", "project", "lower", "measure", "apply"]) {
if (!operations.has(operation)) errors.push(`${plugin.abiFixturePath}:operation:${operation}:missing`);