Add Unity runtime lifecycle evidence

This commit is contained in:
Meta Vens 2026-07-09 00:44:14 +01:00
commit f9711c3532
7 changed files with 271 additions and 7 deletions

View file

@ -279,6 +279,14 @@ Exit criteria:
Recently cut:
- Unity UI Toolkit now declares runtime lifecycle evidence in
`packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json`. The
parity harness validates release, test, and capture lifecycle claims against
`tools/parity/parity-manifest.json`, checks declared evidence paths, and
exports lifecycle status through the conformance index. Current proof covers
incubating UPM package identity and the Aetheria consumer-build smoke; tagged
EveUnity release, Unity EditMode/PlayMode tests, and editor or batchmode
capture remain explicit split blockers.
- Aetheria now has repeatable Unity package consumer-build evidence for the
Eve UI Toolkit runtime. `scripts/run-aetheria-unity-package-smoke.ps1`
verifies Aetheria's Unity `Packages/manifest.json` consumes Eve's surface and

View file

@ -112,6 +112,18 @@ files, then builds the package through Aetheria's Unity-generated project. It is
consumer-build evidence, not a replacement for runtime-owned Unity capture or
release CI.
Unity runtime lifecycle evidence:
`packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json`
declares release, test, and capture lifecycle stages for the UI Toolkit runtime.
The parity harness validates those lifecycle claims against
`tools/parity/parity-manifest.json` and checks that declared evidence paths
exist. The current lifecycle proof is deliberately split:
- release: incubating UPM package identity and import surface exist in Eve;
- test: Aetheria can build the package through Unity's generated project;
- capture: Unity editor or batchmode capture remains a split blocker.
The smoke runner writes:
- `artifacts/parity-smoke/<timestamp>/parity-smoke.md`

View file

@ -32,10 +32,20 @@ Unity consumers should keep verifier coverage that builds a parent surface with
a resolver-backed child, then checks for the embedded visual element rather than
copying child state into a local adapter.
The runtime capability manifest is
`eve-runtime-capability.json`. Its lifecycle section records the current split
evidence:
- release: incubating UPM package identity and import surface;
- test: Aetheria consumer-build smoke through Unity's generated project;
- capture: pending Unity editor or batchmode artifact.
Those lifecycle claims are validated by the parity harness. A missing evidence
path is a runtime capability error, not a README footnote.
For Aetheria, the Unity evidence path is:
- `dotnet build GameCult.Aetheria.State.Unity.csproj --no-restore --nologo -v:minimal`
- `dotnet run --project Aetheria.State.Verify\Aetheria.State.Verify.csproj --no-restore`
- `powershell -ExecutionPolicy Bypass -File ..\..\scripts\run-aetheria-unity-package-smoke.ps1`
Those checks sit alongside Eve's shared browser, Flutter, iOS, Android/Kotlin,
and Rust contract tests, all discoverable from the parity manifest.

View file

@ -42,6 +42,41 @@
"emitter": "EveUiToolkitSurfaceLowerer.EmitCommand",
"clientId": "unity-uitoolkit"
},
"lifecycle": {
"release": {
"ownerRepo": "EveUnity",
"status": "incubating-upm-package",
"packageName": "org.gamecult.eve.unity-uitoolkit",
"packageManifest": "packages/org.gamecult.eve.unity-uitoolkit/package.json",
"evidencePaths": [
"packages/org.gamecult.eve.unity-uitoolkit/package.json",
"packages/org.gamecult.eve.unity-uitoolkit/README.md",
"scripts/run-aetheria-unity-package-smoke.ps1"
],
"pendingProofs": [
"Tagged UPM release cut from EveUnity rather than Eve incubation."
]
},
"test": {
"ownerRepo": "EveUnity",
"status": "consumer-build-smoke",
"command": "powershell -ExecutionPolicy Bypass -File .\\scripts\\run-aetheria-unity-package-smoke.ps1",
"evidencePaths": [
"scripts/run-aetheria-unity-package-smoke.ps1"
],
"pendingProofs": [
"Unity EditMode or PlayMode test assembly runs from the runtime repo."
]
},
"capture": {
"ownerRepo": "EveUnity",
"status": "pending-editor-capture",
"evidencePaths": [],
"pendingProofs": [
"Unity batchmode or editor capture artifact attached to the runtime conformance report."
]
}
},
"capture": {
"status": "semantic",
"note": "Unity UI Toolkit lowers embedded document slots through a resolver-backed child surface host."

View file

@ -12,7 +12,8 @@
"supportedFeatures",
"supportedPlugins",
"unsupportedPlugins",
"commandTransport"
"commandTransport",
"lifecycle"
],
"properties": {
"schema": {
@ -122,6 +123,125 @@
}
},
"additionalProperties": true
},
"lifecycle": {
"type": "object",
"required": [
"release",
"test",
"capture"
],
"properties": {
"release": {
"type": "object",
"required": [
"ownerRepo",
"status",
"evidencePaths"
],
"properties": {
"ownerRepo": {
"type": "string",
"minLength": 1
},
"status": {
"type": "string",
"minLength": 1
},
"command": {
"type": "string"
},
"evidencePaths": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"pendingProofs": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
},
"additionalProperties": true
},
"test": {
"type": "object",
"required": [
"ownerRepo",
"status",
"evidencePaths"
],
"properties": {
"ownerRepo": {
"type": "string",
"minLength": 1
},
"status": {
"type": "string",
"minLength": 1
},
"command": {
"type": "string"
},
"evidencePaths": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"pendingProofs": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
},
"additionalProperties": true
},
"capture": {
"type": "object",
"required": [
"ownerRepo",
"status",
"evidencePaths"
],
"properties": {
"ownerRepo": {
"type": "string",
"minLength": 1
},
"status": {
"type": "string",
"minLength": 1
},
"command": {
"type": "string"
},
"evidencePaths": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"pendingProofs": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
},
"additionalProperties": true
}
},
"additionalProperties": true
}
},
"additionalProperties": true

View file

@ -427,8 +427,9 @@
}
],
"pendingProofs": [
"UPM package and release lifecycle are runtime-owned.",
"Unity test and capture lifecycle are runtime-owned."
"Tagged UPM release is cut from EveUnity rather than Eve incubation.",
"Unity EditMode or PlayMode test assembly runs from the runtime repo.",
"Unity batchmode or editor capture artifact is attached to the runtime conformance report."
]
}
],
@ -741,6 +742,38 @@
}
]
},
"lifecycle": {
"release": {
"ownerRepo": "EveUnity",
"status": "incubating-upm-package",
"evidencePaths": [
"packages/org.gamecult.eve.unity-uitoolkit/package.json",
"packages/org.gamecult.eve.unity-uitoolkit/README.md",
"scripts/run-aetheria-unity-package-smoke.ps1"
],
"pendingProofs": [
"Tagged UPM release cut from EveUnity rather than Eve incubation."
]
},
"test": {
"ownerRepo": "EveUnity",
"status": "consumer-build-smoke",
"evidencePaths": [
"scripts/run-aetheria-unity-package-smoke.ps1"
],
"pendingProofs": [
"Unity EditMode or PlayMode test assembly runs from the runtime repo."
]
},
"capture": {
"ownerRepo": "EveUnity",
"status": "pending-editor-capture",
"evidencePaths": [],
"pendingProofs": [
"Unity batchmode or editor capture artifact attached to the runtime conformance report."
]
}
},
"capture": {
"status": "semantic",
"note": "Unity UI Toolkit lowers embedded document slots through a resolver-backed child surface host."

View file

@ -347,6 +347,7 @@ async function evaluateRuntime(runtime, fixtureResults) {
commandTransportSmokeErrors,
capabilityManifest: runtime.capabilityManifest || null,
capabilityManifestErrors,
lifecycle: runtime.lifecycle || null,
ownerRepo: runtime.ownerRepo || "",
repoRole: runtime.repoRole || "",
graduationTrigger: runtime.graduationTrigger || "",
@ -850,6 +851,7 @@ async function validateRuntimeCapabilityManifest(runtime) {
const supportedPlugins = document.supportedPlugins || [];
const unsupportedPlugins = document.unsupportedPlugins || [];
const commandTransport = document.commandTransport || {};
const lifecycle = document.lifecycle || {};
errors.push(...missingMembers(runtime.supportedFeatures || [], supportedFeatures, `${capabilityManifest.manifestPath}:supportedFeatures`));
errors.push(...comparePluginCapabilityClaims(
@ -880,6 +882,42 @@ async function validateRuntimeCapabilityManifest(runtime) {
}
}
errors.push(...compareRuntimeLifecycleClaims(
runtime.lifecycle || {},
lifecycle,
capabilityManifest.manifestPath,
));
return errors;
}
function compareRuntimeLifecycleClaims(expectedLifecycle, actualLifecycle, manifestPath) {
const errors = [];
for (const stage of ["release", "test", "capture"]) {
const expected = expectedLifecycle[stage];
const actual = actualLifecycle[stage];
const label = `${manifestPath}:lifecycle.${stage}`;
if (!expected) continue;
if (!actual) {
errors.push(`${label}:missing`);
continue;
}
for (const key of ["ownerRepo", "status"]) {
if ((expected[key] || "") !== (actual[key] || "")) {
errors.push(`${label}.${key}:expected ${expected[key] || ""} got ${actual[key] || ""}`);
}
}
errors.push(...missingMembers(expected.evidencePaths || [], actual.evidencePaths || [], `${label}.evidencePaths`));
errors.push(...missingMembers(expected.pendingProofs || [], actual.pendingProofs || [], `${label}.pendingProofs`));
for (const evidencePath of actual.evidencePaths || []) {
if (!existsSync(path.join(repoRoot, evidencePath))) {
errors.push(`${label}.evidencePaths:${evidencePath}:missing`);
}
}
}
return errors;
}
@ -1166,6 +1204,7 @@ function buildConformanceExport(report) {
capabilityManifestPath: runtime.capabilityManifest?.manifestPath || "",
capabilityManifestErrors: runtime.capabilityManifestErrors || [],
commandTransportSchema: runtime.commandTransportSmoke?.schema || "",
lifecycle: runtime.lifecycle,
})),
splitTargets: report.splitTargets || [],
};
@ -1312,7 +1351,7 @@ function renderMarkdown(report) {
lines.push(`| ${provider.title || provider.providerId} | ${provider.status} | ${provider.ownerRepo} | ${provider.scenarioId || ""} | ${provider.scenarioReceiptStates.join(", ")} | ${provider.surfaceIds.join(", ")} | ${provider.commandIds.join(", ")} | ${provider.witnessKinds.join(", ")} | ${missing} |`);
}
lines.push("", "## Runtimes", "", "| Runtime | Status | Owner | Capture | Command Smoke | Required Fixtures | Plugin Fixtures | Features | Plugin Gaps | Unsupported Plugins | Missing |", "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |");
lines.push("", "## Runtimes", "", "| Runtime | Status | Owner | Lifecycle | Capture | Command Smoke | Required Fixtures | Plugin Fixtures | Features | Plugin Gaps | Unsupported Plugins | Missing |", "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |");
for (const runtime of report.runtimes) {
const missing = [
...runtime.missingPaths,
@ -1325,7 +1364,7 @@ function renderMarkdown(report) {
...runtime.capabilityManifestErrors.map(id => `runtime-capability:${id}`),
...runtime.missingIncubationFields.map(id => `metadata:${id}`),
].join(", ");
lines.push(`| ${runtime.title} | ${runtime.status} | ${runtime.ownerRepo} | ${runtime.capture?.status || "unknown"} | ${runtime.commandTransportSmoke?.schema || ""} | ${runtime.requiredFixtures.join(", ")} | ${runtime.pluginFixtures.join(", ")} | ${runtime.supportedFeatures.join(", ")} | ${runtime.pluginCapabilityGaps.join(", ")} | ${runtime.unsupportedPluginNotes.join(", ")} | ${missing} |`);
lines.push(`| ${runtime.title} | ${runtime.status} | ${runtime.ownerRepo} | ${summarizeLifecycle(runtime.lifecycle)} | ${runtime.capture?.status || "unknown"} | ${runtime.commandTransportSmoke?.schema || ""} | ${runtime.requiredFixtures.join(", ")} | ${runtime.pluginFixtures.join(", ")} | ${runtime.supportedFeatures.join(", ")} | ${runtime.pluginCapabilityGaps.join(", ")} | ${runtime.unsupportedPluginNotes.join(", ")} | ${missing} |`);
}
lines.push("", "## Split Readiness", "", "| Target | Status | Owner | Runtimes | Blockers |", "| --- | --- | --- | --- | --- |");
@ -1346,3 +1385,10 @@ function renderMarkdown(report) {
return `${lines.join("\n")}\n`;
}
function summarizeLifecycle(lifecycle) {
if (!lifecycle) return "";
return ["release", "test", "capture"]
.map(stage => lifecycle[stage] ? `${stage}:${lifecycle[stage].status || "unknown"}` : `${stage}:missing`)
.join("<br>");
}