From 88406ea9f0bc813c757eadb2cd098e64a130a87e Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 01:42:51 +0100 Subject: [PATCH] Add EveUnity split handoff proof --- docs/eve-migration-roadmap.md | 8 ++ docs/parity-testing-harness.md | 12 +++ .../README.md | 16 ++- .../eve-runtime-capability.json | 10 +- .../eveunity-split-handoff.json | 65 +++++++++++ scripts/run-eveunity-lifecycle-smoke.ps1 | 8 ++ scripts/run-eveunity-split-handoff-smoke.ps1 | 102 ++++++++++++++++++ scripts/run-parity-harness.ps1 | 1 + ...plit-target-conformance-consumer-smoke.ps1 | 1 + tools/parity/parity-manifest.json | 17 ++- 10 files changed, 232 insertions(+), 8 deletions(-) create mode 100644 packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json create mode 100644 scripts/run-eveunity-split-handoff-smoke.ps1 diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 80d5ba8..c864945 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -280,6 +280,14 @@ Exit criteria: Recently cut: +- EveUnity now has a machine-readable split handoff at + `packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json` and + a verifier at `scripts/run-eveunity-split-handoff-smoke.ps1`. The handoff + names the UPM package body, Unity test lifecycle, Unity capture lifecycle, + contract inputs, forbidden imports, and required external proofs that must + move to EveUnity. It is wired into the runtime capability manifest and + lifecycle smoke, so the remaining split blockers are release/test/capture + ownership rather than an undocumented migration shape. - Split-target conformance consumption now verifies graduation state instead of only split-target presence. The generic consumer supports `--expect-split-target-status`, `--expect-split-target-blocker`, and diff --git a/docs/parity-testing-harness.md b/docs/parity-testing-harness.md index 9e5dacc..dba7335 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -178,6 +178,18 @@ exist. The current lifecycle proof is deliberately split: and Aetheria can build the package through Unity's generated project; - capture: Unity editor or batchmode capture remains a split blocker. +EveUnity split handoff evidence: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\run-eveunity-split-handoff-smoke.ps1 +``` + +That script validates +`packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json` against +the runtime capability manifest. The handoff names the package paths, lifecycle +stages, Eve contracts, forbidden imports, and external proofs that must become +EveUnity-owned before the runtime leaves incubation. + The smoke runner writes: - `artifacts/parity-smoke//parity-smoke.md` diff --git a/packages/org.gamecult.eve.unity-uitoolkit/README.md b/packages/org.gamecult.eve.unity-uitoolkit/README.md index 4d971b0..5e7c575 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/README.md +++ b/packages/org.gamecult.eve.unity-uitoolkit/README.md @@ -45,6 +45,13 @@ evidence: Those lifecycle claims are validated by the parity harness. A missing evidence path is a runtime capability error, not a README footnote. +The split handoff manifest is `eveunity-split-handoff.json`. It is the +machine-readable map of what EveUnity must carry out of Eve incubation: the UPM +package body, the Unity test lifecycle, the Unity capture lifecycle, the Eve +contracts it consumes, and the forbidden imports it must not use as shortcuts. +It does not make the package ready to split by itself; it makes the remaining +release, test, and capture blockers inspectable. + From the repository root, run the split lifecycle smoke with: ```powershell @@ -52,10 +59,11 @@ powershell -ExecutionPolicy Bypass -File .\scripts\run-eveunity-lifecycle-smoke. ``` That smoke validates `eve-runtime-capability.json`, checks lifecycle evidence -paths, and runs the Aetheria Unity package consumer-build smoke. Pass -`-RunUnityEditMode` when the local Unity editor should also execute the -incubating batchmode EditMode test runner. The final tagged UPM release, -batchmode runner ownership, and capture artifact still graduate to `EveUnity`. +paths, runs the Aetheria Unity package consumer-build smoke, and verifies the +split handoff manifest. Pass `-RunUnityEditMode` when the local Unity editor +should also execute the incubating batchmode EditMode test runner. The final +tagged UPM release, batchmode runner ownership, and capture artifact still +graduate to `EveUnity`. For Aetheria, the Unity evidence path is: diff --git a/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json b/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json index e2fa8a7..fa3db65 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json +++ b/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json @@ -7,7 +7,11 @@ "ownerRepo": "EveUnity", "currentHostRepo": "Eve", "splitTarget": "EveUnity", - "graduationTrigger": "Split when Unity package has independent UPM release, plugin projection adapter behavior, and Unity test/capture lifecycle." + "graduationTrigger": "Split when Unity package has independent UPM release, plugin projection adapter behavior, and Unity test/capture lifecycle.", + "splitHandoff": { + "manifestPath": "packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json", + "smokeCommand": "powershell -ExecutionPolicy Bypass -File .\\scripts\\run-eveunity-split-handoff-smoke.ps1" + } }, "supportedFeatures": [ "embeddedDocuments" @@ -65,7 +69,9 @@ "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef", "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs", "scripts/run-aetheria-unity-editmode-tests.ps1", - "scripts/run-aetheria-unity-package-smoke.ps1" + "scripts/run-aetheria-unity-package-smoke.ps1", + "scripts/run-eveunity-split-handoff-smoke.ps1", + "packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json" ], "pendingProofs": [ "Unity batchmode EditMode runner is owned by EveUnity rather than Eve incubation." diff --git a/packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json b/packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json new file mode 100644 index 0000000..3b9f46c --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json @@ -0,0 +1,65 @@ +{ + "schema": "gamecult.eve.runtime_split_handoff.v1", + "splitTarget": "EveUnity", + "ownerRepo": "EveUnity", + "currentHostRepo": "Eve", + "runtimeId": "unity-uitoolkit", + "packageName": "org.gamecult.eve.unity-uitoolkit", + "sourcePackageRoot": "packages/org.gamecult.eve.unity-uitoolkit", + "destinationPackageRoot": "packages/org.gamecult.eve.unity-uitoolkit", + "authorityBoundary": "EveUnity owns native Unity projection, UPM package lifecycle, Unity test runners, Unity capture, and projection adapters for advertised plugin capabilities. Eve keeps the surface, command, provider advertisement, plugin advertisement, and conformance contracts.", + "moveSets": [ + { + "id": "upm-package", + "stage": "release", + "currentPaths": [ + "packages/org.gamecult.eve.unity-uitoolkit/package.json", + "packages/org.gamecult.eve.unity-uitoolkit/README.md", + "packages/org.gamecult.eve.unity-uitoolkit/Runtime", + "packages/org.gamecult.eve.unity-uitoolkit/Tests" + ], + "destinationOwner": "EveUnity", + "replacementProof": "Tagged UPM release cut from EveUnity rather than Eve incubation." + }, + { + "id": "unity-test-lifecycle", + "stage": "test", + "currentPaths": [ + "scripts/run-eveunity-lifecycle-smoke.ps1", + "scripts/run-aetheria-unity-package-smoke.ps1", + "scripts/run-aetheria-unity-editmode-tests.ps1", + "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef", + "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs" + ], + "destinationOwner": "EveUnity", + "replacementProof": "Unity batchmode EditMode runner is owned by EveUnity rather than Eve incubation." + }, + { + "id": "unity-capture-lifecycle", + "stage": "capture", + "currentPaths": [], + "destinationOwner": "EveUnity", + "replacementProof": "Unity batchmode or editor capture artifact attached to the runtime conformance report." + } + ], + "contractInputs": [ + "gamecult.eve.surface.v1", + "gamecult.eve.command.v1", + "gamecult.eve.provider_advertisement.v1", + "gamecult.eve.plugin_advertisement.v1", + "gamecult.eve.plugin.v1", + "gamecult.eve.conformance_export.v1" + ], + "forbiddenImports": [ + "provider internals", + "plugin semantic internals", + "Aetheria daemon state as Unity package authority", + "Sai or Norn implementation state as Unity projection authority" + ], + "requiredExternalProofs": [ + "EveUnity publishes a tagged UPM release.", + "EveUnity owns the Unity batchmode EditMode runner.", + "EveUnity attaches Unity editor or batchmode capture artifacts to conformance output.", + "Aetheria consumes EveUnity through the package contract without importing Eve source layout." + ] +} diff --git a/scripts/run-eveunity-lifecycle-smoke.ps1 b/scripts/run-eveunity-lifecycle-smoke.ps1 index 4401f8c..f54e575 100644 --- a/scripts/run-eveunity-lifecycle-smoke.ps1 +++ b/scripts/run-eveunity-lifecycle-smoke.ps1 @@ -36,6 +36,9 @@ if ($manifest.incubation.currentHostRepo -ne "Eve") { if ($manifest.incubation.splitTarget -ne "EveUnity") { throw "Unexpected EveUnity split target: $($manifest.incubation.splitTarget)" } +if (-not $manifest.incubation.splitHandoff.manifestPath) { + throw "EveUnity manifest missing split handoff path" +} foreach ($feature in @("embeddedDocuments")) { if (-not ($manifest.supportedFeatures -contains $feature)) { @@ -89,6 +92,11 @@ if ($LASTEXITCODE -ne 0) { throw "Aetheria Unity package smoke failed with exit code $LASTEXITCODE" } +& (Join-Path $projectRoot "scripts\run-eveunity-split-handoff-smoke.ps1") +if ($LASTEXITCODE -ne 0) { + throw "EveUnity split handoff smoke failed with exit code $LASTEXITCODE" +} + if ($RunUnityEditMode) { & (Join-Path $projectRoot "scripts\run-aetheria-unity-editmode-tests.ps1") if ($LASTEXITCODE -ne 0) { diff --git a/scripts/run-eveunity-split-handoff-smoke.ps1 b/scripts/run-eveunity-split-handoff-smoke.ps1 new file mode 100644 index 0000000..d6efbf1 --- /dev/null +++ b/scripts/run-eveunity-split-handoff-smoke.ps1 @@ -0,0 +1,102 @@ +param( + [string] $HandoffPath = "packages\org.gamecult.eve.unity-uitoolkit\eveunity-split-handoff.json", + [string] $CapabilityPath = "packages\org.gamecult.eve.unity-uitoolkit\eve-runtime-capability.json" +) + +$ErrorActionPreference = "Stop" + +$projectRoot = Split-Path -Parent $PSScriptRoot +$absoluteHandoffPath = if ([System.IO.Path]::IsPathRooted($HandoffPath)) { + $HandoffPath +} else { + Join-Path $projectRoot $HandoffPath +} +$absoluteCapabilityPath = if ([System.IO.Path]::IsPathRooted($CapabilityPath)) { + $CapabilityPath +} else { + Join-Path $projectRoot $CapabilityPath +} + +if (-not (Test-Path -LiteralPath $absoluteHandoffPath)) { + throw "EveUnity split handoff manifest not found: $absoluteHandoffPath" +} +if (-not (Test-Path -LiteralPath $absoluteCapabilityPath)) { + throw "EveUnity runtime capability manifest not found: $absoluteCapabilityPath" +} + +$handoff = Get-Content -LiteralPath $absoluteHandoffPath -Raw | ConvertFrom-Json +$capability = Get-Content -LiteralPath $absoluteCapabilityPath -Raw | ConvertFrom-Json + +if ($handoff.schema -ne "gamecult.eve.runtime_split_handoff.v1") { + throw "Unexpected EveUnity split handoff schema: $($handoff.schema)" +} +if ($handoff.splitTarget -ne "EveUnity") { + throw "Unexpected EveUnity split handoff target: $($handoff.splitTarget)" +} +if ($handoff.ownerRepo -ne "EveUnity") { + throw "Unexpected EveUnity split handoff owner: $($handoff.ownerRepo)" +} +if ($handoff.currentHostRepo -ne "Eve") { + throw "Unexpected EveUnity split handoff host: $($handoff.currentHostRepo)" +} +if ($handoff.runtimeId -ne "unity-uitoolkit") { + throw "Unexpected EveUnity split handoff runtime: $($handoff.runtimeId)" +} +if ($capability.incubation.splitHandoff.manifestPath -ne $HandoffPath.Replace("\", "/")) { + $expectedPath = $HandoffPath.Replace("\", "/") + throw "EveUnity capability manifest does not point at split handoff. Expected $expectedPath got $($capability.incubation.splitHandoff.manifestPath)" +} + +$expectedStages = @("release", "test", "capture") +foreach ($stage in $expectedStages) { + if (-not $capability.lifecycle.$stage) { + throw "EveUnity capability lifecycle missing stage: $stage" + } +} + +$moveSets = @($handoff.moveSets) +foreach ($stage in $expectedStages) { + $moveSet = $moveSets | Where-Object { $_.stage -eq $stage } | Select-Object -First 1 + if (-not $moveSet) { + throw "EveUnity split handoff missing move set for stage: $stage" + } + if ($moveSet.destinationOwner -ne "EveUnity") { + throw "EveUnity split handoff stage $stage has unexpected destination owner: $($moveSet.destinationOwner)" + } + if (-not $moveSet.replacementProof) { + throw "EveUnity split handoff stage $stage missing replacement proof" + } + + $pendingProofs = @($capability.lifecycle.$stage.pendingProofs) + if (-not ($pendingProofs -contains $moveSet.replacementProof)) { + throw "EveUnity split handoff stage $stage replacement proof is not mirrored in runtime capability pending proofs: $($moveSet.replacementProof)" + } + + foreach ($relativePath in @($moveSet.currentPaths)) { + $absolutePath = Join-Path $projectRoot $relativePath + if (-not (Test-Path -LiteralPath $absolutePath)) { + throw "EveUnity split handoff stage $stage references missing current path: $relativePath" + } + } +} + +foreach ($contract in @( + "gamecult.eve.surface.v1", + "gamecult.eve.command.v1", + "gamecult.eve.provider_advertisement.v1", + "gamecult.eve.plugin_advertisement.v1", + "gamecult.eve.plugin.v1", + "gamecult.eve.conformance_export.v1" +)) { + if (-not (@($handoff.contractInputs) -contains $contract)) { + throw "EveUnity split handoff missing contract input: $contract" + } +} + +foreach ($proof in @($handoff.requiredExternalProofs)) { + if (-not $proof) { + throw "EveUnity split handoff contains an empty required external proof" + } +} + +Write-Host "EveUnity split handoff smoke passed: $absoluteHandoffPath" diff --git a/scripts/run-parity-harness.ps1 b/scripts/run-parity-harness.ps1 index f2061f8..21aea16 100644 --- a/scripts/run-parity-harness.ps1 +++ b/scripts/run-parity-harness.ps1 @@ -14,6 +14,7 @@ $env:EVE_PARITY_OUTPUT = if ([System.IO.Path]::IsPathRooted($OutputDirectory)) { Push-Location $projectRoot try { node .\tools\parity\run-parity.mjs + & (Join-Path $PSScriptRoot "run-eveunity-split-handoff-smoke.ps1") & (Join-Path $PSScriptRoot "run-conformance-consumer-smoke.ps1") & (Join-Path $PSScriptRoot "run-plugin-owner-conformance-consumer-smoke.ps1") & (Join-Path $PSScriptRoot "run-runtime-owner-conformance-consumer-smoke.ps1") diff --git a/scripts/run-split-target-conformance-consumer-smoke.ps1 b/scripts/run-split-target-conformance-consumer-smoke.ps1 index 424232f..87e176e 100644 --- a/scripts/run-split-target-conformance-consumer-smoke.ps1 +++ b/scripts/run-split-target-conformance-consumer-smoke.ps1 @@ -40,6 +40,7 @@ try { --expect-split-target EveUnity ` --expect-split-target-status EveUnity:incubating ` --expect-split-target-proof "EveUnity:Unity lifecycle evidence is declared" ` + --expect-split-target-proof "EveUnity:EveUnity split handoff is machine-readable" ` --expect-split-target-blocker "EveUnity:Tagged UPM release" ` --expect-split-target-blocker "EveUnity:Unity batchmode EditMode runner" ` --expect-split-target-blocker "EveUnity:Unity batchmode or editor capture artifact" diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index 8304323..8488443 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -503,6 +503,15 @@ "scripts/run-aetheria-unity-package-smoke.ps1", "scripts/run-aetheria-unity-editmode-tests.ps1" ] + }, + { + "description": "EveUnity split handoff is machine-readable and smoke-checked.", + "status": "passed", + "evidencePaths": [ + "packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json", + "scripts/run-eveunity-split-handoff-smoke.ps1", + "packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json" + ] } ], "pendingProofs": [ @@ -884,7 +893,9 @@ "packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitProjectionAdapter.cs", "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef", "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs", - "scripts/run-aetheria-unity-editmode-tests.ps1" + "packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json", + "scripts/run-aetheria-unity-editmode-tests.ps1", + "scripts/run-eveunity-split-handoff-smoke.ps1" ], "expectedSourceSymbols": [ { @@ -946,7 +957,9 @@ "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef", "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs", "scripts/run-aetheria-unity-editmode-tests.ps1", - "scripts/run-aetheria-unity-package-smoke.ps1" + "scripts/run-aetheria-unity-package-smoke.ps1", + "scripts/run-eveunity-split-handoff-smoke.ps1", + "packages/org.gamecult.eve.unity-uitoolkit/eveunity-split-handoff.json" ], "pendingProofs": [ "Unity batchmode EditMode runner is owned by EveUnity rather than Eve incubation."