Add runtime split handoff schema

This commit is contained in:
Meta Vens 2026-07-09 11:53:46 +01:00
commit 8de1dced44
10 changed files with 171 additions and 2 deletions

View file

@ -418,6 +418,11 @@ Recently cut:
and command transport, does not treat Aetheria product paths as generic
runtime source, names the required Eve contracts, and keeps capture as an
external proof until EveUnity owns it.
- Runtime split handoff documents now have a catalog-backed schema,
`gamecult.eve.runtime_split_handoff.v1`. EveConformance exports that schema
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.
- 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

View file

@ -216,6 +216,10 @@ Aetheria's current Electron client, and `replacement-required` records mark
move sets where no generic runtime source exists yet. Consumer smokes can
assert them with
`--expect-split-handoff-move <splitTarget:runtimeId:moveSetId:pathKind:status:path-substring>`.
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.
After generating the parity report, `run-parity-harness.ps1` copies
`artifacts/conformance/latest` into

View file

@ -1023,6 +1023,9 @@
"splitHandoffPath": {
"type": "string"
},
"splitHandoffErrors": {
"type": "array"
},
"splitHandoffExportPath": {
"type": "string"
},

View file

@ -0,0 +1,133 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "gamecult.eve.runtime_split_handoff.v1",
"title": "Eve Runtime Split Handoff",
"type": "object",
"required": [
"schema",
"splitTarget",
"ownerRepo",
"currentHostRepo",
"runtimeId",
"sourcePackageRoot",
"destinationPackageRoot",
"authorityBoundary",
"moveSets",
"contractInputs",
"forbiddenImports",
"requiredExternalProofs"
],
"properties": {
"schema": {
"const": "gamecult.eve.runtime_split_handoff.v1"
},
"splitTarget": {
"type": "string",
"minLength": 1
},
"ownerRepo": {
"type": "string",
"minLength": 1
},
"currentHostRepo": {
"type": "string",
"minLength": 1
},
"runtimeId": {
"type": "string",
"minLength": 1
},
"packageName": {
"type": "string"
},
"sourcePackageRoot": {
"type": "string",
"minLength": 1
},
"destinationPackageRoot": {
"type": "string",
"minLength": 1
},
"authorityBoundary": {
"type": "string",
"minLength": 1
},
"moveSets": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"id",
"stage",
"destinationOwner",
"replacementProof"
],
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"stage": {
"type": "string",
"minLength": 1
},
"currentPaths": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"observedProviderPaths": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"replacementRequired": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"destinationOwner": {
"type": "string",
"minLength": 1
},
"replacementProof": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": true
}
},
"contractInputs": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
}
},
"forbiddenImports": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"requiredExternalProofs": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
}
}
},
"additionalProperties": true
}

View file

@ -40,6 +40,7 @@ try {
--expect-schema gamecult.eve.plugin_abi.response.v1 `
--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-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 `

View file

@ -87,7 +87,8 @@ foreach ($contract in @(
"gamecult.eve.runtime_release_request.v1",
"gamecult.eve.runtime_capture_request.v1",
"gamecult.eve.provider_advertisement.v1",
"gamecult.eve.runtime_capability.v1"
"gamecult.eve.runtime_capability.v1",
"gamecult.eve.runtime_split_handoff.v1"
)) {
if (-not (@($handoff.contractInputs) -contains $contract)) {
throw "EveConformance handoff missing contract input: $contract"
@ -118,6 +119,7 @@ if (Test-Path -LiteralPath (Join-Path $absoluteExportDirectory "index.json")) {
"gamecult.eve.provider_scenario.v1",
"gamecult.eve.provider_advertisement.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",

View file

@ -43,6 +43,7 @@ node $consumerScript $consumerExport `
--expect-runtime direct2d `
--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-runtime-status web:active `
--expect-runtime-status windows-flutter:active `
--expect-runtime-status linux-flutter:active `

View file

@ -25,6 +25,7 @@
"schemas/gamecult.eve.provider_scenario.v1.schema.json",
"schemas/gamecult.eve.provider_advertisement.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",
@ -80,7 +81,8 @@
"gamecult.eve.runtime_release_request.v1",
"gamecult.eve.runtime_capture_request.v1",
"gamecult.eve.provider_advertisement.v1",
"gamecult.eve.runtime_capability.v1"
"gamecult.eve.runtime_capability.v1",
"gamecult.eve.runtime_split_handoff.v1"
],
"forbiddenImports": [
"provider internals as conformance truth",

View file

@ -19,6 +19,7 @@
"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",
"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",

View file

@ -305,6 +305,7 @@ async function evaluateRuntime(runtime, fixtureResults) {
const capabilityManifestErrors = await validateRuntimeCapabilityManifest(runtime);
const capabilityManifestDocument = await readRuntimeCapabilityManifestDocument(runtime);
const splitHandoffPath = await readRuntimeSplitHandoffPath(runtime);
const splitHandoffErrors = await validateRuntimeSplitHandoff(runtime, splitHandoffPath);
const localProviderCatalogErrors = await validateRuntimeLocalProviderCatalog(runtime);
const missingIncubationFields = requiredIncubationFields(runtime).filter(field => !runtime[field]);
const pluginCapabilityGaps = collectPluginCapabilityGaps(runtime, fixtureResults);
@ -316,6 +317,7 @@ async function evaluateRuntime(runtime, fixtureResults) {
if (runtime.kind === "active" && missingRequiredFeatures.length) status = "missing-required-feature";
if (runtime.kind === "active" && commandTransportSmokeErrors.length) status = "missing-command-transport-smoke";
if (runtime.kind === "active" && capabilityManifestErrors.length) status = "invalid-runtime-capability";
if (runtime.kind === "active" && splitHandoffErrors.length) status = "invalid-runtime-split-handoff";
if (runtime.kind === "active" && localProviderCatalogErrors.length) status = "invalid-local-provider-catalog";
if (runtime.kind === "active" && missingIncubationFields.length) status = "missing-incubation-metadata";
if (runtime.kind === "active" && pluginCapabilityGaps.length && status === "active") status = "active-with-capability-gaps";
@ -354,6 +356,7 @@ async function evaluateRuntime(runtime, fixtureResults) {
capabilityManifest: runtime.capabilityManifest || null,
capabilityManifestErrors,
splitHandoffPath,
splitHandoffErrors,
localProviderCatalog: runtime.localProviderCatalog || null,
localProviderCatalogErrors,
lifecycle: runtime.lifecycle || capabilityManifestDocument?.lifecycle || null,
@ -437,6 +440,18 @@ async function readRuntimeSplitHandoffPath(runtime) {
}
}
async function validateRuntimeSplitHandoff(runtime, splitHandoffPath) {
if (!splitHandoffPath) return [];
return validateJsonDocument(
manifest.schemas?.["gamecult.eve.runtime_split_handoff.v1"],
splitHandoffPath,
{
schema: "gamecult.eve.runtime_split_handoff.v1",
runtimeId: runtime.id,
},
);
}
function normalizePath(candidate) {
return candidate.replaceAll("\\", "/").replace(/^\.\//, "");
}
@ -1804,6 +1819,7 @@ function buildConformanceExport(report) {
capabilityManifestPath: runtime.capabilityManifest?.manifestPath || "",
capabilityManifestErrors: runtime.capabilityManifestErrors || [],
splitHandoffPath: runtime.splitHandoffPath || "",
splitHandoffErrors: runtime.splitHandoffErrors || [],
splitHandoffExportPath: makeHandoffExportPath("runtime", runtime.id, runtime.splitHandoffPath || ""),
commandTransportSchema: runtimeCommandTransportSchema(runtime),
captureStatus: runtime.capture?.status || "",
@ -1975,6 +1991,7 @@ function collectCapabilityGaps(report) {
...(runtime.missingRequiredFeatures || []).map(id => `feature:${id}`),
...(runtime.commandTransportSmokeErrors || []).map(id => `command-smoke:${id}`),
...(runtime.capabilityManifestErrors || []).map(id => `runtime-capability:${id}`),
...(runtime.splitHandoffErrors || []).map(id => `runtime-split-handoff:${id}`),
...(runtime.localProviderCatalogErrors || []).map(id => `local-provider-catalog:${id}`),
...(runtime.pluginCapabilityGaps || []).map(id => `plugin-capability:${id}`),
]) {