From 7c7cfcde8e8bd0f608b640b445d640dd453854ba Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 17:00:58 +0100 Subject: [PATCH] Add generic runtime capture controls --- docs/eve-migration-roadmap.md | 6 ++ .../eve-runtime-capability.json | 10 ++- .../eve-electron/eve-runtime-capability.json | 10 ++- .../eve-tui/eve-runtime-capability.json | 10 ++- .../eve-runtime-capability.json | 10 ++- ...cult.eve.conformance_export.v1.schema.json | 21 +++++ ...cult.eve.runtime_capability.v1.schema.json | 21 +++++ scripts/run-eveelectron-capture-smoke.ps1 | 14 ++-- scripts/run-evetui-capture-smoke.ps1 | 14 ++-- scripts/run-eveunity-scene-capture-smoke.ps1 | 14 ++-- .../run-eveunity-uitoolkit-capture-smoke.ps1 | 14 ++-- scripts/run-parity-harness.ps1 | 36 +++++++++ ...ntime-owner-conformance-consumer-smoke.ps1 | 14 +++- .../eveelectron-capture-artifact.test.mjs | 21 +++++ .../eveelectron-capture-contract.mjs | 33 +++++++- .../eveelectron-capture-contract.test.mjs | 17 ++++ tools/evetui/evetui-capture-artifact.test.mjs | 24 ++++++ tools/evetui/evetui-capture-contract.mjs | 33 +++++++- tools/evetui/evetui-capture-contract.test.mjs | 17 ++++ tools/eveunity/eveunity-capture-contract.mjs | 33 +++++++- .../eveunity-capture-contract.test.mjs | 33 ++++++++ .../eveunity-scene-capture-artifact.test.mjs | 21 +++++ ...eunity-uitoolkit-capture-artifact.test.mjs | 21 +++++ tools/parity/parity-manifest.json | 80 +++++++++++++++---- tools/parity/run-parity.mjs | 11 +++ 25 files changed, 486 insertions(+), 52 deletions(-) diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index f6f3b2b..54e5614 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -1007,6 +1007,12 @@ Recently cut: accepted and denied command receipts through `eve.world_smoke.command_receipt.v1`, so replay proof is no longer only an Aetheria product concern. +- Runtime semantic capture now uses the same generic world control. UI Toolkit, + Unity scene, Electron shell, and TUI capture contracts keep Aetheria as the + primary pressure fixture while declaring `eve.world-smoke` as an additional + accepted provider surface. The parity harness emits separate + `eve-world-smoke` JSON projection/grid artifacts for those runtimes, and the + runtime-owner conformance smoke asserts them from the exported pack. - Sai, Norn, and TeX now have plugin ABI fixtures for describe, validate, project, lower, measure, and apply behavior. The parity harness validates those fixtures against plugin manifests, so Eve proves the ABI shape without 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 efec482..3118155 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json +++ b/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json @@ -61,7 +61,8 @@ "packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs", "packages/org.gamecult.eve.unity-uitoolkit/Runtime/TeXMathUiToolkitProjectionAdapter.cs", "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs", - "web/fixtures/aetheria.provider-advertisement.json" + "web/fixtures/aetheria.provider-advertisement.json", + "web/fixtures/eve-world-smoke.provider-advertisement.json" ] } ], @@ -199,6 +200,13 @@ "conformanceAttachment": "runtime.captureArtifacts[]", "requiredProvider": "aetheria", "requiredSurface": "aetheria.daemon.game", + "additionalProviderSurfaces": [ + { + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "purpose": "Generic non-product interactive-world capture control." + } + ], "authority": "Capture observes runtime projection only; provider state, command acceptance, and receipts stay provider-owned.", "publishProof": "Unity editor or batchmode capture artifact is attached to the runtime conformance report from EveUnity." }, diff --git a/runtimes/incubating/eve-electron/eve-runtime-capability.json b/runtimes/incubating/eve-electron/eve-runtime-capability.json index 081237e..d3b83f7 100644 --- a/runtimes/incubating/eve-electron/eve-runtime-capability.json +++ b/runtimes/incubating/eve-electron/eve-runtime-capability.json @@ -60,7 +60,8 @@ "runtimes/incubating/eve-electron/src/eve-electron-shell.mjs", "runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs", "web/fixtures/sai-vn-surface.json", - "web/fixtures/aetheria.provider-advertisement.json" + "web/fixtures/aetheria.provider-advertisement.json", + "web/fixtures/eve-world-smoke.provider-advertisement.json" ] } ], @@ -143,6 +144,13 @@ "conformanceAttachment": "runtime.captureArtifacts[]", "requiredProvider": "aetheria", "requiredSurface": "aetheria.daemon.game", + "additionalProviderSurfaces": [ + { + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "purpose": "Generic non-product interactive-world capture control." + } + ], "authority": "Capture observes Electron projection only; provider state, command acceptance, and receipts stay provider-owned.", "publishProof": "Electron window capture artifact is attached to the runtime conformance report from EveElectron." }, diff --git a/runtimes/incubating/eve-tui/eve-runtime-capability.json b/runtimes/incubating/eve-tui/eve-runtime-capability.json index 8e98bd3..13ccb60 100644 --- a/runtimes/incubating/eve-tui/eve-runtime-capability.json +++ b/runtimes/incubating/eve-tui/eve-runtime-capability.json @@ -65,7 +65,8 @@ "runtimes/incubating/eve-tui/src/eve-tui-shell.mjs", "runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs", "web/fixtures/sai-vn-surface.json", - "web/fixtures/aetheria.provider-advertisement.json" + "web/fixtures/aetheria.provider-advertisement.json", + "web/fixtures/eve-world-smoke.provider-advertisement.json" ] } ], @@ -148,6 +149,13 @@ "conformanceAttachment": "runtime.captureArtifacts[]", "requiredProvider": "aetheria", "requiredSurface": "aetheria.daemon.game", + "additionalProviderSurfaces": [ + { + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "purpose": "Generic non-product interactive-world capture control." + } + ], "authority": "Capture observes terminal projection only; provider state, command acceptance, and receipts stay provider-owned.", "publishProof": "Terminal cell-grid JSON capture artifact is attached to the runtime conformance report from EveTui." }, diff --git a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json index 1802b90..6e255fd 100644 --- a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json +++ b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json @@ -62,7 +62,8 @@ "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs", - "web/fixtures/aetheria.provider-advertisement.json" + "web/fixtures/aetheria.provider-advertisement.json", + "web/fixtures/eve-world-smoke.provider-advertisement.json" ] } ], @@ -163,6 +164,13 @@ "conformanceAttachment": "runtime.captureArtifacts[]", "requiredProvider": "aetheria", "requiredSurface": "aetheria.daemon.game", + "additionalProviderSurfaces": [ + { + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "purpose": "Generic non-product interactive-world capture control." + } + ], "authority": "Capture observes Unity scene projection only; provider state, command acceptance, and receipts stay provider-owned.", "publishProof": "Unity scene screenshot or frame-capture PNG artifact is attached to the runtime conformance report from EveUnity." }, diff --git a/schemas/gamecult.eve.conformance_export.v1.schema.json b/schemas/gamecult.eve.conformance_export.v1.schema.json index 716eec5..9d1821b 100644 --- a/schemas/gamecult.eve.conformance_export.v1.schema.json +++ b/schemas/gamecult.eve.conformance_export.v1.schema.json @@ -893,6 +893,27 @@ "requiredSurface": { "type": "string" }, + "additionalProviderSurfaces": { + "type": "array", + "items": { + "type": "object", + "required": ["providerId", "surfaceId"], + "properties": { + "providerId": { + "type": "string", + "minLength": 1 + }, + "surfaceId": { + "type": "string", + "minLength": 1 + }, + "purpose": { + "type": "string" + } + }, + "additionalProperties": true + } + }, "authority": { "type": "string" }, diff --git a/schemas/gamecult.eve.runtime_capability.v1.schema.json b/schemas/gamecult.eve.runtime_capability.v1.schema.json index dbeacd0..da06631 100644 --- a/schemas/gamecult.eve.runtime_capability.v1.schema.json +++ b/schemas/gamecult.eve.runtime_capability.v1.schema.json @@ -573,6 +573,27 @@ "type": "string", "minLength": 1 }, + "additionalProviderSurfaces": { + "type": "array", + "items": { + "type": "object", + "required": ["providerId", "surfaceId"], + "properties": { + "providerId": { + "type": "string", + "minLength": 1 + }, + "surfaceId": { + "type": "string", + "minLength": 1 + }, + "purpose": { + "type": "string" + } + }, + "additionalProperties": true + } + }, "authority": { "type": "string", "minLength": 1 diff --git a/scripts/run-eveelectron-capture-smoke.ps1 b/scripts/run-eveelectron-capture-smoke.ps1 index 52c3564..2f01197 100644 --- a/scripts/run-eveelectron-capture-smoke.ps1 +++ b/scripts/run-eveelectron-capture-smoke.ps1 @@ -4,7 +4,11 @@ param( [string] $SurfacePath = "web\fixtures\aetheria-world-surface.json", [string] $OutputPath = "artifacts\eveelectron-capture\latest\electron-shell-projection.json", [string] $RequestOutputPath = "artifacts\eveelectron-capture\latest\capture-request.json", - [string] $Stamp = "latest" + [string] $Stamp = "latest", + [string] $ExpectedProviderId = "aetheria", + [string] $ExpectedSurfaceId = "aetheria.daemon.game", + [string] $ExpectedCommandBoundary = "aetheria.daemon.commands", + [string] $ExpectedReceiptSchema = "aetheria.eve_command_acceptance_status.v1" ) $ErrorActionPreference = "Stop" @@ -55,10 +59,10 @@ $projection = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Js foreach ($expectation in @( @{ field = "schema"; value = "gamecult.eve.electron_shell_projection.v1" }, @{ field = "runtimeId"; value = "electron-shell" }, - @{ field = "providerId"; value = "aetheria" }, - @{ field = "surfaceId"; value = "aetheria.daemon.game" }, - @{ field = "commandBoundary"; value = "aetheria.daemon.commands" }, - @{ field = "receiptSchema"; value = "aetheria.eve_command_acceptance_status.v1" }, + @{ field = "providerId"; value = $ExpectedProviderId }, + @{ field = "surfaceId"; value = $ExpectedSurfaceId }, + @{ field = "commandBoundary"; value = $ExpectedCommandBoundary }, + @{ field = "receiptSchema"; value = $ExpectedReceiptSchema }, @{ field = "artifactKind"; value = "json-projection" }, @{ field = "captureKind"; value = "electron-shell-projection-json" } )) { diff --git a/scripts/run-evetui-capture-smoke.ps1 b/scripts/run-evetui-capture-smoke.ps1 index 496fdb1..f7004ac 100644 --- a/scripts/run-evetui-capture-smoke.ps1 +++ b/scripts/run-evetui-capture-smoke.ps1 @@ -4,7 +4,11 @@ param( [string] $SurfacePath = "web\fixtures\aetheria-world-surface.json", [string] $OutputPath = "artifacts\evetui-capture\latest\tui-grid.json", [string] $RequestOutputPath = "artifacts\evetui-capture\latest\capture-request.json", - [string] $Stamp = "latest" + [string] $Stamp = "latest", + [string] $ExpectedProviderId = "aetheria", + [string] $ExpectedSurfaceId = "aetheria.daemon.game", + [string] $ExpectedCommandBoundary = "aetheria.daemon.commands", + [string] $ExpectedReceiptSchema = "aetheria.eve_command_acceptance_status.v1" ) $ErrorActionPreference = "Stop" @@ -55,10 +59,10 @@ $grid = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Json foreach ($expectation in @( @{ field = "schema"; value = "gamecult.eve.tui_grid.v1" }, @{ field = "runtimeId"; value = "tui" }, - @{ field = "providerId"; value = "aetheria" }, - @{ field = "surfaceId"; value = "aetheria.daemon.game" }, - @{ field = "commandBoundary"; value = "aetheria.daemon.commands" }, - @{ field = "receiptSchema"; value = "aetheria.eve_command_acceptance_status.v1" }, + @{ field = "providerId"; value = $ExpectedProviderId }, + @{ field = "surfaceId"; value = $ExpectedSurfaceId }, + @{ field = "commandBoundary"; value = $ExpectedCommandBoundary }, + @{ field = "receiptSchema"; value = $ExpectedReceiptSchema }, @{ field = "artifactKind"; value = "json-grid" }, @{ field = "captureKind"; value = "terminal-cell-grid" } )) { diff --git a/scripts/run-eveunity-scene-capture-smoke.ps1 b/scripts/run-eveunity-scene-capture-smoke.ps1 index 1a91715..67879fb 100644 --- a/scripts/run-eveunity-scene-capture-smoke.ps1 +++ b/scripts/run-eveunity-scene-capture-smoke.ps1 @@ -4,7 +4,11 @@ param( [string] $SurfacePath = "web\fixtures\aetheria-world-surface.json", [string] $OutputPath = "artifacts\eveunity-scene-capture\latest\unity-scene-projection.json", [string] $RequestOutputPath = "artifacts\eveunity-scene-capture\latest\capture-request.json", - [string] $Stamp = "latest" + [string] $Stamp = "latest", + [string] $ExpectedProviderId = "aetheria", + [string] $ExpectedSurfaceId = "aetheria.daemon.game", + [string] $ExpectedCommandBoundary = "aetheria.daemon.commands", + [string] $ExpectedReceiptSchema = "aetheria.eve_command_acceptance_status.v1" ) $ErrorActionPreference = "Stop" @@ -55,10 +59,10 @@ $projection = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Js foreach ($expectation in @( @{ field = "schema"; value = "gamecult.eve.unity_scene_projection.v1" }, @{ field = "runtimeId"; value = "unity-scene" }, - @{ field = "providerId"; value = "aetheria" }, - @{ field = "surfaceId"; value = "aetheria.daemon.game" }, - @{ field = "commandBoundary"; value = "aetheria.daemon.commands" }, - @{ field = "receiptSchema"; value = "aetheria.eve_command_acceptance_status.v1" }, + @{ field = "providerId"; value = $ExpectedProviderId }, + @{ field = "surfaceId"; value = $ExpectedSurfaceId }, + @{ field = "commandBoundary"; value = $ExpectedCommandBoundary }, + @{ field = "receiptSchema"; value = $ExpectedReceiptSchema }, @{ field = "artifactKind"; value = "json-projection" }, @{ field = "captureKind"; value = "unity-scene-projection-json" } )) { diff --git a/scripts/run-eveunity-uitoolkit-capture-smoke.ps1 b/scripts/run-eveunity-uitoolkit-capture-smoke.ps1 index eb1ab03..632dad7 100644 --- a/scripts/run-eveunity-uitoolkit-capture-smoke.ps1 +++ b/scripts/run-eveunity-uitoolkit-capture-smoke.ps1 @@ -4,7 +4,11 @@ param( [string] $SurfacePath = "web\fixtures\aetheria-world-surface.json", [string] $OutputPath = "artifacts\eveunity-uitoolkit-capture\latest\unity-uitoolkit-projection.json", [string] $RequestOutputPath = "artifacts\eveunity-uitoolkit-capture\latest\capture-request.json", - [string] $Stamp = "latest" + [string] $Stamp = "latest", + [string] $ExpectedProviderId = "aetheria", + [string] $ExpectedSurfaceId = "aetheria.daemon.game", + [string] $ExpectedCommandBoundary = "aetheria.daemon.commands", + [string] $ExpectedReceiptSchema = "aetheria.eve_command_acceptance_status.v1" ) $ErrorActionPreference = "Stop" @@ -55,10 +59,10 @@ $projection = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Js foreach ($expectation in @( @{ field = "schema"; value = "gamecult.eve.unity_uitoolkit_projection.v1" }, @{ field = "runtimeId"; value = "unity-uitoolkit" }, - @{ field = "providerId"; value = "aetheria" }, - @{ field = "surfaceId"; value = "aetheria.daemon.game" }, - @{ field = "commandBoundary"; value = "aetheria.daemon.commands" }, - @{ field = "receiptSchema"; value = "aetheria.eve_command_acceptance_status.v1" }, + @{ field = "providerId"; value = $ExpectedProviderId }, + @{ field = "surfaceId"; value = $ExpectedSurfaceId }, + @{ field = "commandBoundary"; value = $ExpectedCommandBoundary }, + @{ field = "receiptSchema"; value = $ExpectedReceiptSchema }, @{ field = "artifactKind"; value = "json-projection" }, @{ field = "captureKind"; value = "unity-uitoolkit-projection-json" } )) { diff --git a/scripts/run-parity-harness.ps1 b/scripts/run-parity-harness.ps1 index c3e2560..cb43d68 100644 --- a/scripts/run-parity-harness.ps1 +++ b/scripts/run-parity-harness.ps1 @@ -27,7 +27,43 @@ try { -Port 8894 ` -SkipBuild & (Join-Path $PSScriptRoot "run-eveunity-uitoolkit-capture-smoke.ps1") + & (Join-Path $PSScriptRoot "run-eveunity-uitoolkit-capture-smoke.ps1") ` + -AdvertisementPath "web\fixtures\eve-world-smoke.provider-advertisement.json" ` + -SurfacePath "web\fixtures\eve-world-smoke-surface.json" ` + -OutputPath "artifacts\eveunity-uitoolkit-capture\latest\eve-world-smoke-unity-uitoolkit-projection.json" ` + -RequestOutputPath "artifacts\eveunity-uitoolkit-capture\latest\eve-world-smoke-capture-request.json" ` + -ExpectedProviderId "eve.world-smoke" ` + -ExpectedSurfaceId "eve.world-smoke.surface" ` + -ExpectedCommandBoundary "eve.world-smoke.commands" ` + -ExpectedReceiptSchema "eve.world_smoke.command_receipt.v1" + & (Join-Path $PSScriptRoot "run-eveunity-scene-capture-smoke.ps1") ` + -AdvertisementPath "web\fixtures\eve-world-smoke.provider-advertisement.json" ` + -SurfacePath "web\fixtures\eve-world-smoke-surface.json" ` + -OutputPath "artifacts\eveunity-scene-capture\latest\eve-world-smoke-unity-scene-projection.json" ` + -RequestOutputPath "artifacts\eveunity-scene-capture\latest\eve-world-smoke-capture-request.json" ` + -ExpectedProviderId "eve.world-smoke" ` + -ExpectedSurfaceId "eve.world-smoke.surface" ` + -ExpectedCommandBoundary "eve.world-smoke.commands" ` + -ExpectedReceiptSchema "eve.world_smoke.command_receipt.v1" + & (Join-Path $PSScriptRoot "run-eveelectron-capture-smoke.ps1") ` + -AdvertisementPath "web\fixtures\eve-world-smoke.provider-advertisement.json" ` + -SurfacePath "web\fixtures\eve-world-smoke-surface.json" ` + -OutputPath "artifacts\eveelectron-capture\latest\eve-world-smoke-electron-shell-projection.json" ` + -RequestOutputPath "artifacts\eveelectron-capture\latest\eve-world-smoke-capture-request.json" ` + -ExpectedProviderId "eve.world-smoke" ` + -ExpectedSurfaceId "eve.world-smoke.surface" ` + -ExpectedCommandBoundary "eve.world-smoke.commands" ` + -ExpectedReceiptSchema "eve.world_smoke.command_receipt.v1" & (Join-Path $PSScriptRoot "run-evetui-capture-smoke.ps1") + & (Join-Path $PSScriptRoot "run-evetui-capture-smoke.ps1") ` + -AdvertisementPath "web\fixtures\eve-world-smoke.provider-advertisement.json" ` + -SurfacePath "web\fixtures\eve-world-smoke-surface.json" ` + -OutputPath "artifacts\evetui-capture\latest\eve-world-smoke-tui-grid.json" ` + -RequestOutputPath "artifacts\evetui-capture\latest\eve-world-smoke-capture-request.json" ` + -ExpectedProviderId "eve.world-smoke" ` + -ExpectedSurfaceId "eve.world-smoke.surface" ` + -ExpectedCommandBoundary "eve.world-smoke.commands" ` + -ExpectedReceiptSchema "eve.world_smoke.command_receipt.v1" node .\tools\parity\run-parity.mjs & (Join-Path $PSScriptRoot "run-eveconformance-handoff-smoke.ps1") & (Join-Path $PSScriptRoot "run-plugin-handoff-smoke.ps1") diff --git a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 index 270d85c..9b71548 100644 --- a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 +++ b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 @@ -145,12 +145,16 @@ node $consumerScript $consumerExport ` --expect-runtime-capture-status android-flutter:adb-png ` --expect-runtime-capture-status unity-uitoolkit:json-projection ` --expect-runtime-capture-artifact unity-uitoolkit:json-projection:gamecult.eve.unity_uitoolkit_projection.v1:aetheria:aetheria.daemon.game ` + --expect-runtime-capture-artifact unity-uitoolkit:json-projection:gamecult.eve.unity_uitoolkit_projection.v1:eve.world-smoke:eve.world-smoke.surface ` --expect-runtime-capture-status unity-scene:json-projection ` --expect-runtime-capture-artifact unity-scene:json-projection:gamecult.eve.unity_scene_projection.v1:aetheria:aetheria.daemon.game ` + --expect-runtime-capture-artifact unity-scene:json-projection:gamecult.eve.unity_scene_projection.v1:eve.world-smoke:eve.world-smoke.surface ` --expect-runtime-capture-status electron-shell:json-projection ` --expect-runtime-capture-artifact electron-shell:json-projection:gamecult.eve.electron_shell_projection.v1:aetheria:aetheria.daemon.game ` + --expect-runtime-capture-artifact electron-shell:json-projection:gamecult.eve.electron_shell_projection.v1:eve.world-smoke:eve.world-smoke.surface ` --expect-runtime-capture-status tui:json-grid ` --expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:aetheria:aetheria.daemon.game ` + --expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:eve.world-smoke:eve.world-smoke.surface ` --expect-runtime-capture-status direct2d:missing ` --expect-runtime-capture-probe unity-uitoolkit:semantic-artifact-present-capture-pending:png:EveUnity ` --expect-runtime-capture-probe unity-scene:semantic-artifact-present-capture-pending:png:EveUnity ` @@ -209,6 +213,8 @@ node $consumerScript $consumerExport ` --expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.conformanceAttachment:runtime.captureArtifacts[]" ` --expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.requiredProvider:aetheria" ` --expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.requiredSurface:aetheria.daemon.game" ` + --expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.additionalProviderSurfaces.0.providerId:eve.world-smoke" ` + --expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.additionalProviderSurfaces.0.surfaceId:eve.world-smoke.surface" ` --expect-runtime-lifecycle-status unity-scene:release:pending-unity-scene-package ` --expect-runtime-lifecycle-status unity-scene:test:provider-shell-contract-skeleton ` --expect-runtime-lifecycle-status unity-scene:capture:pending-unity-scene-capture ` @@ -233,6 +239,8 @@ node $consumerScript $consumerExport ` --expect-runtime-lifecycle-field "unity-scene:capture:captureContract.requestBuilder:tools/eveunity/eveunity-capture-contract.mjs" ` --expect-runtime-lifecycle-field "unity-scene:capture:captureContract.advertisementPath:web/fixtures/aetheria.provider-advertisement.json" ` --expect-runtime-lifecycle-field "unity-scene:capture:captureContract.requiredSurface:aetheria.daemon.game" ` + --expect-runtime-lifecycle-field "unity-scene:capture:captureContract.additionalProviderSurfaces.0.providerId:eve.world-smoke" ` + --expect-runtime-lifecycle-field "unity-scene:capture:captureContract.additionalProviderSurfaces.0.surfaceId:eve.world-smoke.surface" ` --expect-runtime-lifecycle-status electron-shell:release:pending-electron-package ` --expect-runtime-lifecycle-status electron-shell:test:provider-shell-contract-skeleton ` --expect-runtime-lifecycle-status electron-shell:capture:pending-electron-window-capture ` @@ -252,6 +260,8 @@ node $consumerScript $consumerExport ` --expect-runtime-lifecycle-field "electron-shell:capture:captureContract.requestBuilder:tools/eveelectron/eveelectron-capture-contract.mjs" ` --expect-runtime-lifecycle-field "electron-shell:capture:captureContract.advertisementPath:web/fixtures/aetheria.provider-advertisement.json" ` --expect-runtime-lifecycle-field "electron-shell:capture:captureContract.requiredSurface:aetheria.daemon.game" ` + --expect-runtime-lifecycle-field "electron-shell:capture:captureContract.additionalProviderSurfaces.0.providerId:eve.world-smoke" ` + --expect-runtime-lifecycle-field "electron-shell:capture:captureContract.additionalProviderSurfaces.0.surfaceId:eve.world-smoke.surface" ` --expect-runtime-lifecycle-status tui:release:pending-tui-package ` --expect-runtime-lifecycle-status tui:test:provider-shell-contract-skeleton ` --expect-runtime-lifecycle-status tui:capture:cell-grid-json-capture ` @@ -272,7 +282,9 @@ node $consumerScript $consumerExport ` --expect-runtime-lifecycle-field "tui:capture:captureContract.requestSchema:gamecult.eve.runtime_capture_request.v1" ` --expect-runtime-lifecycle-field "tui:capture:captureContract.requestBuilder:tools/evetui/evetui-capture-contract.mjs" ` --expect-runtime-lifecycle-field "tui:capture:captureContract.advertisementPath:web/fixtures/aetheria.provider-advertisement.json" ` - --expect-runtime-lifecycle-field "tui:capture:captureContract.requiredSurface:aetheria.daemon.game" + --expect-runtime-lifecycle-field "tui:capture:captureContract.requiredSurface:aetheria.daemon.game" ` + --expect-runtime-lifecycle-field "tui:capture:captureContract.additionalProviderSurfaces.0.providerId:eve.world-smoke" ` + --expect-runtime-lifecycle-field "tui:capture:captureContract.additionalProviderSurfaces.0.surfaceId:eve.world-smoke.surface" if ($LASTEXITCODE -ne 0) { throw "Runtime owner conformance consumer smoke failed with exit code $LASTEXITCODE" diff --git a/tools/eveelectron/eveelectron-capture-artifact.test.mjs b/tools/eveelectron/eveelectron-capture-artifact.test.mjs index 3f00264..a9d98b9 100644 --- a/tools/eveelectron/eveelectron-capture-artifact.test.mjs +++ b/tools/eveelectron/eveelectron-capture-artifact.test.mjs @@ -6,6 +6,8 @@ import { buildElectronCaptureArtifact } from "./eveelectron-capture-artifact.mjs const advertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8")); const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-electron/eve-runtime-capability.json", import.meta.url), "utf8")); const surfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria-world-surface.json", import.meta.url), "utf8")); +const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8")); +const worldSmokeSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke-surface.json", import.meta.url), "utf8")); test("builds Electron shell projection capture artifact from provider advertisement and surface", () => { const { request, projection } = buildElectronCaptureArtifact({ @@ -28,3 +30,22 @@ test("builds Electron shell projection capture artifact from provider advertisem assert.equal(projection.captureKind, "electron-shell-projection-json"); assert.equal(projection.root.id, "aetheria.daemon.game.root"); }); + +test("builds Electron shell projection capture artifact from a generic world provider", () => { + const { projection } = buildElectronCaptureArtifact({ + advertisement: worldSmokeAdvertisement, + capabilityManifest, + surfaceDocument: worldSmokeSurfaceDocument, + advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json", + capabilityManifestPath: "runtimes/incubating/eve-electron/eve-runtime-capability.json", + stamp: "smoke", + }); + + assert.equal(projection.schema, "gamecult.eve.electron_shell_projection.v1"); + assert.equal(projection.runtimeId, "electron-shell"); + assert.equal(projection.providerId, "eve.world-smoke"); + assert.equal(projection.surfaceId, "eve.world-smoke.surface"); + assert.equal(projection.commandBoundary, "eve.world-smoke.commands"); + assert.equal(projection.receiptSchema, "eve.world_smoke.command_receipt.v1"); + assert.equal(projection.root.id, "eve.world-smoke.root"); +}); diff --git a/tools/eveelectron/eveelectron-capture-contract.mjs b/tools/eveelectron/eveelectron-capture-contract.mjs index 559d97d..f7d7938 100644 --- a/tools/eveelectron/eveelectron-capture-contract.mjs +++ b/tools/eveelectron/eveelectron-capture-contract.mjs @@ -21,16 +21,17 @@ export function buildElectronCaptureRequest({ throw new Error("capability manifest is missing lifecycle.capture.captureContract"); } - if (advertisement.providerId !== captureContract.requiredProvider) { + const captureSurface = selectCaptureSurface(captureContract, advertisement.providerId); + if (!captureSurface) { throw new Error( - `capture provider mismatch: expected ${captureContract.requiredProvider} got ${advertisement.providerId || "missing"}`, + `capture provider mismatch: expected ${captureProviderList(captureContract)} got ${advertisement.providerId || "missing"}`, ); } const targetId = captureContract.targetId || captureContract.runtimeId; - const surface = (advertisement.surfaces || []).find(candidate => candidate.surfaceId === captureContract.requiredSurface); + const surface = (advertisement.surfaces || []).find(candidate => candidate.surfaceId === captureSurface.surfaceId); if (!surface) { - throw new Error(`capture surface not advertised: ${captureContract.requiredSurface}`); + throw new Error(`capture surface not advertised: ${captureSurface.surfaceId}`); } const loweringTargets = surface.worldInteraction?.loweringTargets || []; @@ -78,6 +79,30 @@ function replaceStamp(pattern, stamp) { return pattern.replace("{stamp}", stamp); } +function selectCaptureSurface(captureContract, providerId) { + return captureSurfaceClaims(captureContract).find(claim => claim.providerId === providerId) || null; +} + +function captureProviderList(captureContract) { + return captureSurfaceClaims(captureContract).map(claim => claim.providerId).filter(Boolean).join(", ") || "missing"; +} + +function captureSurfaceClaims(captureContract) { + return [ + { + providerId: captureContract.requiredProvider || "", + surfaceId: captureContract.requiredSurface || "", + }, + ...(Array.isArray(captureContract.additionalProviderSurfaces) ? captureContract.additionalProviderSurfaces : []), + ] + .filter(claim => claim && typeof claim === "object") + .map(claim => ({ + providerId: String(claim.providerId || ""), + surfaceId: String(claim.surfaceId || ""), + })) + .filter(claim => claim.providerId && claim.surfaceId); +} + function parseArgs(argv) { const args = {}; for (let index = 0; index < argv.length; index += 1) { diff --git a/tools/eveelectron/eveelectron-capture-contract.test.mjs b/tools/eveelectron/eveelectron-capture-contract.test.mjs index 3587ceb..339c36f 100644 --- a/tools/eveelectron/eveelectron-capture-contract.test.mjs +++ b/tools/eveelectron/eveelectron-capture-contract.test.mjs @@ -4,6 +4,7 @@ import test from "node:test"; import { buildElectronCaptureRequest } from "./eveelectron-capture-contract.mjs"; const advertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8")); +const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8")); const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-electron/eve-runtime-capability.json", import.meta.url), "utf8")); test("builds Electron shell capture request from provider advertisement", () => { @@ -28,6 +29,22 @@ test("builds Electron shell capture request from provider advertisement", () => assert.match(request.authority, /provider state/); }); +test("builds Electron shell capture request from a declared generic world provider", () => { + const request = buildElectronCaptureRequest({ + advertisement: worldSmokeAdvertisement, + capabilityManifest, + advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json", + capabilityManifestPath: "runtimes/incubating/eve-electron/eve-runtime-capability.json", + stamp: "smoke", + }); + + assert.equal(request.runtimeId, "electron-shell"); + assert.equal(request.providerId, "eve.world-smoke"); + assert.equal(request.surfaceId, "eve.world-smoke.surface"); + assert.equal(request.commandBoundary, "eve.world-smoke.commands"); + assert.equal(request.receiptSchema, "eve.world_smoke.command_receipt.v1"); +}); + test("rejects capture request when the surface does not advertise Electron shell", () => { const withoutTarget = structuredClone(advertisement); const surface = withoutTarget.surfaces.find(candidate => candidate.surfaceId === "aetheria.daemon.game"); diff --git a/tools/evetui/evetui-capture-artifact.test.mjs b/tools/evetui/evetui-capture-artifact.test.mjs index 4371d00..fea9dd3 100644 --- a/tools/evetui/evetui-capture-artifact.test.mjs +++ b/tools/evetui/evetui-capture-artifact.test.mjs @@ -6,6 +6,8 @@ import { buildTuiCaptureArtifact } from "./evetui-capture-artifact.mjs"; const advertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8")); const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-tui/eve-runtime-capability.json", import.meta.url), "utf8")); const surfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria-world-surface.json", import.meta.url), "utf8")); +const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8")); +const worldSmokeSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke-surface.json", import.meta.url), "utf8")); test("builds TUI JSON grid capture artifact from provider advertisement and surface", () => { const { request, grid } = buildTuiCaptureArtifact({ @@ -31,3 +33,25 @@ test("builds TUI JSON grid capture artifact from provider advertisement and surf assert.equal(grid.width, 72); assert.ok(grid.lines.some(line => line.includes("aetheria.daemon.game"))); }); + +test("builds TUI JSON grid capture artifact from a generic world provider", () => { + const { grid } = buildTuiCaptureArtifact({ + advertisement: worldSmokeAdvertisement, + capabilityManifest, + surfaceDocument: worldSmokeSurfaceDocument, + advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json", + capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json", + stamp: "smoke", + width: 72, + }); + + assert.equal(grid.schema, "gamecult.eve.tui_grid.v1"); + assert.equal(grid.runtimeId, "tui"); + assert.equal(grid.providerId, "eve.world-smoke"); + assert.equal(grid.surfaceId, "eve.world-smoke.surface"); + assert.equal(grid.commandBoundary, "eve.world-smoke.commands"); + assert.equal(grid.receiptSchema, "eve.world_smoke.command_receipt.v1"); + assert.equal(grid.artifactKind, "json-grid"); + assert.equal(grid.captureKind, "terminal-cell-grid"); + assert.ok(grid.lines.some(line => line.includes("eve.world-smoke.surface"))); +}); diff --git a/tools/evetui/evetui-capture-contract.mjs b/tools/evetui/evetui-capture-contract.mjs index 9ff1dd5..9a074af 100644 --- a/tools/evetui/evetui-capture-contract.mjs +++ b/tools/evetui/evetui-capture-contract.mjs @@ -21,16 +21,17 @@ export function buildTuiCaptureRequest({ throw new Error("capability manifest is missing lifecycle.capture.captureContract"); } - if (advertisement.providerId !== captureContract.requiredProvider) { + const captureSurface = selectCaptureSurface(captureContract, advertisement.providerId); + if (!captureSurface) { throw new Error( - `capture provider mismatch: expected ${captureContract.requiredProvider} got ${advertisement.providerId || "missing"}`, + `capture provider mismatch: expected ${captureProviderList(captureContract)} got ${advertisement.providerId || "missing"}`, ); } const targetId = captureContract.targetId || captureContract.runtimeId; - const surface = (advertisement.surfaces || []).find(candidate => candidate.surfaceId === captureContract.requiredSurface); + const surface = (advertisement.surfaces || []).find(candidate => candidate.surfaceId === captureSurface.surfaceId); if (!surface) { - throw new Error(`capture surface not advertised: ${captureContract.requiredSurface}`); + throw new Error(`capture surface not advertised: ${captureSurface.surfaceId}`); } const loweringTargets = surface.worldInteraction?.loweringTargets || []; @@ -78,6 +79,30 @@ function replaceStamp(pattern, stamp) { return pattern.replace("{stamp}", stamp); } +function selectCaptureSurface(captureContract, providerId) { + return captureSurfaceClaims(captureContract).find(claim => claim.providerId === providerId) || null; +} + +function captureProviderList(captureContract) { + return captureSurfaceClaims(captureContract).map(claim => claim.providerId).filter(Boolean).join(", ") || "missing"; +} + +function captureSurfaceClaims(captureContract) { + return [ + { + providerId: captureContract.requiredProvider || "", + surfaceId: captureContract.requiredSurface || "", + }, + ...(Array.isArray(captureContract.additionalProviderSurfaces) ? captureContract.additionalProviderSurfaces : []), + ] + .filter(claim => claim && typeof claim === "object") + .map(claim => ({ + providerId: String(claim.providerId || ""), + surfaceId: String(claim.surfaceId || ""), + })) + .filter(claim => claim.providerId && claim.surfaceId); +} + function parseArgs(argv) { const args = {}; for (let index = 0; index < argv.length; index += 1) { diff --git a/tools/evetui/evetui-capture-contract.test.mjs b/tools/evetui/evetui-capture-contract.test.mjs index fde68f8..73ede9b 100644 --- a/tools/evetui/evetui-capture-contract.test.mjs +++ b/tools/evetui/evetui-capture-contract.test.mjs @@ -4,6 +4,7 @@ import test from "node:test"; import { buildTuiCaptureRequest } from "./evetui-capture-contract.mjs"; const advertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8")); +const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8")); const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-tui/eve-runtime-capability.json", import.meta.url), "utf8")); test("builds TUI capture request from provider advertisement", () => { @@ -30,6 +31,22 @@ test("builds TUI capture request from provider advertisement", () => { assert.match(request.authority, /provider state/); }); +test("builds TUI capture request from a declared generic world provider", () => { + const request = buildTuiCaptureRequest({ + advertisement: worldSmokeAdvertisement, + capabilityManifest, + advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json", + capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json", + stamp: "smoke", + }); + + assert.equal(request.runtimeId, "tui"); + assert.equal(request.providerId, "eve.world-smoke"); + assert.equal(request.surfaceId, "eve.world-smoke.surface"); + assert.equal(request.commandBoundary, "eve.world-smoke.commands"); + assert.equal(request.receiptSchema, "eve.world_smoke.command_receipt.v1"); +}); + test("rejects capture request when the surface does not advertise TUI", () => { const withoutTarget = structuredClone(advertisement); const surface = withoutTarget.surfaces.find(candidate => candidate.surfaceId === "aetheria.daemon.game"); diff --git a/tools/eveunity/eveunity-capture-contract.mjs b/tools/eveunity/eveunity-capture-contract.mjs index 8fdbbd7..ad59bd1 100644 --- a/tools/eveunity/eveunity-capture-contract.mjs +++ b/tools/eveunity/eveunity-capture-contract.mjs @@ -21,16 +21,17 @@ export function buildUnityCaptureRequest({ throw new Error("capability manifest is missing lifecycle.capture.captureContract"); } - if (advertisement.providerId !== captureContract.requiredProvider) { + const captureSurface = selectCaptureSurface(captureContract, advertisement.providerId); + if (!captureSurface) { throw new Error( - `capture provider mismatch: expected ${captureContract.requiredProvider} got ${advertisement.providerId || "missing"}`, + `capture provider mismatch: expected ${captureProviderList(captureContract)} got ${advertisement.providerId || "missing"}`, ); } const targetId = captureContract.targetId || captureContract.runtimeId; - const surface = (advertisement.surfaces || []).find(candidate => candidate.surfaceId === captureContract.requiredSurface); + const surface = (advertisement.surfaces || []).find(candidate => candidate.surfaceId === captureSurface.surfaceId); if (!surface) { - throw new Error(`capture surface not advertised: ${captureContract.requiredSurface}`); + throw new Error(`capture surface not advertised: ${captureSurface.surfaceId}`); } const loweringTargets = surface.worldInteraction?.loweringTargets || []; @@ -78,6 +79,30 @@ function replaceStamp(pattern, stamp) { return pattern.replace("{stamp}", stamp); } +function selectCaptureSurface(captureContract, providerId) { + return captureSurfaceClaims(captureContract).find(claim => claim.providerId === providerId) || null; +} + +function captureProviderList(captureContract) { + return captureSurfaceClaims(captureContract).map(claim => claim.providerId).filter(Boolean).join(", ") || "missing"; +} + +function captureSurfaceClaims(captureContract) { + return [ + { + providerId: captureContract.requiredProvider || "", + surfaceId: captureContract.requiredSurface || "", + }, + ...(Array.isArray(captureContract.additionalProviderSurfaces) ? captureContract.additionalProviderSurfaces : []), + ] + .filter(claim => claim && typeof claim === "object") + .map(claim => ({ + providerId: String(claim.providerId || ""), + surfaceId: String(claim.surfaceId || ""), + })) + .filter(claim => claim.providerId && claim.surfaceId); +} + function parseArgs(argv) { const args = {}; for (let index = 0; index < argv.length; index += 1) { diff --git a/tools/eveunity/eveunity-capture-contract.test.mjs b/tools/eveunity/eveunity-capture-contract.test.mjs index 4848f33..a5049b3 100644 --- a/tools/eveunity/eveunity-capture-contract.test.mjs +++ b/tools/eveunity/eveunity-capture-contract.test.mjs @@ -4,6 +4,7 @@ import test from "node:test"; import { buildUnityCaptureRequest } from "./eveunity-capture-contract.mjs"; const advertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8")); +const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8")); const capabilityManifest = JSON.parse(readFileSync(new URL("../../packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json", import.meta.url), "utf8")); const sceneCapabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-unity-scene/eve-runtime-capability.json", import.meta.url), "utf8")); @@ -29,6 +30,22 @@ test("builds Unity UI Toolkit capture request from provider advertisement", () = assert.match(request.authority, /provider state/); }); +test("builds Unity UI Toolkit capture request from a declared generic world provider", () => { + const request = buildUnityCaptureRequest({ + advertisement: worldSmokeAdvertisement, + capabilityManifest, + advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json", + capabilityManifestPath: "packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json", + stamp: "smoke", + }); + + assert.equal(request.runtimeId, "unity-uitoolkit"); + assert.equal(request.providerId, "eve.world-smoke"); + assert.equal(request.surfaceId, "eve.world-smoke.surface"); + assert.equal(request.commandBoundary, "eve.world-smoke.commands"); + assert.equal(request.receiptSchema, "eve.world_smoke.command_receipt.v1"); +}); + test("rejects a capture request when the surface does not advertise the runtime target", () => { const withoutTarget = structuredClone(advertisement); const surface = withoutTarget.surfaces.find(candidate => candidate.surfaceId === "aetheria.daemon.game"); @@ -61,3 +78,19 @@ test("builds Unity scene capture request from provider advertisement", () => { assert.equal(request.artifactPath, "artifacts/eveunity-scene-capture/smoke/unity-scene.png"); assert.match(request.authority, /provider state/); }); + +test("builds Unity scene capture request from a declared generic world provider", () => { + const request = buildUnityCaptureRequest({ + advertisement: worldSmokeAdvertisement, + capabilityManifest: sceneCapabilityManifest, + advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json", + capabilityManifestPath: "runtimes/incubating/eve-unity-scene/eve-runtime-capability.json", + stamp: "smoke", + }); + + assert.equal(request.runtimeId, "unity-scene"); + assert.equal(request.providerId, "eve.world-smoke"); + assert.equal(request.surfaceId, "eve.world-smoke.surface"); + assert.equal(request.commandBoundary, "eve.world-smoke.commands"); + assert.equal(request.receiptSchema, "eve.world_smoke.command_receipt.v1"); +}); diff --git a/tools/eveunity/eveunity-scene-capture-artifact.test.mjs b/tools/eveunity/eveunity-scene-capture-artifact.test.mjs index 449190e..65a7b3f 100644 --- a/tools/eveunity/eveunity-scene-capture-artifact.test.mjs +++ b/tools/eveunity/eveunity-scene-capture-artifact.test.mjs @@ -8,6 +8,8 @@ const unitySceneProjectionSchema = JSON.parse(readFileSync(new URL("../../schema const aetheriaAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8")); const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-unity-scene/eve-runtime-capability.json", import.meta.url), "utf8")); const aetheriaSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria-world-surface.json", import.meta.url), "utf8")); +const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8")); +const worldSmokeSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke-surface.json", import.meta.url), "utf8")); const saiAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/sai-vn.provider-advertisement.json", import.meta.url), "utf8")); const saiSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/sai-vn-surface.json", import.meta.url), "utf8")); @@ -35,6 +37,25 @@ test("builds Unity scene projection capture artifact from provider advertisement assert.equal(projection.root.children[1].children[0].sceneObjectKind, "world-projection-node"); }); +test("builds Unity scene capture artifact for a generic world provider", () => { + const { projection } = buildUnitySceneCaptureArtifact({ + advertisement: worldSmokeAdvertisement, + capabilityManifest, + surfaceDocument: worldSmokeSurfaceDocument, + advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json", + capabilityManifestPath: "runtimes/incubating/eve-unity-scene/eve-runtime-capability.json", + stamp: "smoke", + }); + + assert.deepEqual(validateSchemaSubset(unitySceneProjectionSchema, projection), []); + assert.equal(projection.providerId, "eve.world-smoke"); + assert.equal(projection.surfaceId, "eve.world-smoke.surface"); + assert.equal(projection.commandBoundary, "eve.world-smoke.commands"); + assert.equal(projection.receiptSchema, "eve.world_smoke.command_receipt.v1"); + assert.equal(projection.root.id, "eve.world-smoke.root"); + assert.equal(projection.root.children[1].sceneObjectKind, "world-projection-node"); +}); + test("marks Sai required and Norn TeX optional sidecar plugin projections", () => { const advertisement = structuredClone(saiAdvertisement); advertisement.surfaces[0].worldInteraction = { diff --git a/tools/eveunity/eveunity-uitoolkit-capture-artifact.test.mjs b/tools/eveunity/eveunity-uitoolkit-capture-artifact.test.mjs index 090e6d7..a912f3f 100644 --- a/tools/eveunity/eveunity-uitoolkit-capture-artifact.test.mjs +++ b/tools/eveunity/eveunity-uitoolkit-capture-artifact.test.mjs @@ -8,6 +8,8 @@ const unityUiToolkitProjectionSchema = JSON.parse(readFileSync(new URL("../../sc const aetheriaAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8")); const capabilityManifest = JSON.parse(readFileSync(new URL("../../packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json", import.meta.url), "utf8")); const aetheriaSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria-world-surface.json", import.meta.url), "utf8")); +const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8")); +const worldSmokeSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke-surface.json", import.meta.url), "utf8")); const saiAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/sai-vn.provider-advertisement.json", import.meta.url), "utf8")); const saiSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/sai-vn-surface.json", import.meta.url), "utf8")); @@ -36,6 +38,25 @@ test("builds Unity UI Toolkit projection capture artifact from provider advertis assert.deepEqual(projection.root.classNames.slice(0, 2), ["eve-component", "eve-kind-surface"]); }); +test("builds Unity UI Toolkit capture artifact for a generic world provider", () => { + const { projection } = buildUnityUiToolkitCaptureArtifact({ + advertisement: worldSmokeAdvertisement, + capabilityManifest, + surfaceDocument: worldSmokeSurfaceDocument, + advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json", + capabilityManifestPath: "packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json", + stamp: "smoke", + }); + + assert.deepEqual(validateSchemaSubset(unityUiToolkitProjectionSchema, projection), []); + assert.equal(projection.providerId, "eve.world-smoke"); + assert.equal(projection.surfaceId, "eve.world-smoke.surface"); + assert.equal(projection.commandBoundary, "eve.world-smoke.commands"); + assert.equal(projection.receiptSchema, "eve.world_smoke.command_receipt.v1"); + assert.equal(projection.root.id, "eve.world-smoke.root"); + assert.equal(projection.root.children[1].visualElementKind, "VisualElement"); +}); + test("marks Sai required and Norn TeX optional sidecar plugin UI projections", () => { const advertisement = structuredClone(saiAdvertisement); advertisement.surfaces[0].worldInteraction = { diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index fc43738..710fe78 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -785,7 +785,7 @@ "manifestPath": "runtimes/incubating/eve-electron/eve-runtime-capability.json" }, "splitHandoffPath": "runtimes/incubating/eve-electron/eveelectron-split-handoff.json", - "requiredFixtures": ["embedded-surface", "aetheria-world"], + "requiredFixtures": ["embedded-surface", "aetheria-world", "eve-world-smoke"], "pluginFixtures": ["sai-vn"], "supportedFeatures": ["providerAdvertisements", "commandTransport", "surfaceTreeProjection", "embeddedDocuments", "pluginProjection"], "supportedPlugins": [ @@ -814,7 +814,8 @@ "runtimes/incubating/eve-electron/src/eve-electron-shell.mjs", "runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs", "web/fixtures/sai-vn-surface.json", - "web/fixtures/aetheria.provider-advertisement.json" + "web/fixtures/aetheria.provider-advertisement.json", + "web/fixtures/eve-world-smoke.provider-advertisement.json" ] } ], @@ -851,7 +852,7 @@ }, { "path": "tools/eveelectron/eveelectron-capture-contract.test.mjs", - "contains": ["builds Electron shell capture request from provider advertisement", "rejects capture request when the surface does not advertise Electron shell", "electron-shell", "aetheria.daemon.commands"] + "contains": ["builds Electron shell capture request from provider advertisement", "builds Electron shell capture request from a declared generic world provider", "rejects capture request when the surface does not advertise Electron shell", "electron-shell", "aetheria.daemon.commands", "eve.world-smoke.commands"] }, { "path": "tools/eveelectron/eveelectron-capture-artifact.mjs", @@ -859,7 +860,7 @@ }, { "path": "tools/eveelectron/eveelectron-capture-artifact.test.mjs", - "contains": ["builds Electron shell projection capture artifact", "gamecult.eve.electron_shell_projection.v1", "electron-shell-projection-json"] + "contains": ["builds Electron shell projection capture artifact", "builds Electron shell projection capture artifact from a generic world provider", "gamecult.eve.electron_shell_projection.v1", "electron-shell-projection-json", "eve.world-smoke.commands"] }, { "path": "tools/eveelectron/eveelectron-release-contract.mjs", @@ -883,6 +884,16 @@ "surfaceId": "aetheria.daemon.game", "sourceSurfacePath": "web/fixtures/aetheria-world-surface.json", "requestPath": "artifacts/eveelectron-capture/latest/capture-request.json" + }, + { + "kind": "json-projection", + "schema": "gamecult.eve.electron_shell_projection.v1", + "path": "artifacts/eveelectron-capture/latest/eve-world-smoke-electron-shell-projection.json", + "runtimeId": "electron-shell", + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "sourceSurfacePath": "web/fixtures/eve-world-smoke-surface.json", + "requestPath": "artifacts/eveelectron-capture/latest/eve-world-smoke-capture-request.json" } ] } @@ -900,7 +911,7 @@ "manifestPath": "runtimes/incubating/eve-tui/eve-runtime-capability.json" }, "splitHandoffPath": "runtimes/incubating/eve-tui/evetui-split-handoff.json", - "requiredFixtures": ["embedded-surface", "aetheria-world"], + "requiredFixtures": ["embedded-surface", "aetheria-world", "eve-world-smoke"], "pluginFixtures": ["sai-vn"], "supportedFeatures": ["providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "embeddedDocuments", "pluginProjection"], "supportedPlugins": [ @@ -930,7 +941,8 @@ "runtimes/incubating/eve-tui/src/eve-tui-shell.mjs", "runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs", "web/fixtures/sai-vn-surface.json", - "web/fixtures/aetheria.provider-advertisement.json" + "web/fixtures/aetheria.provider-advertisement.json", + "web/fixtures/eve-world-smoke.provider-advertisement.json" ] } ], @@ -968,7 +980,7 @@ }, { "path": "tools/evetui/evetui-capture-contract.test.mjs", - "contains": ["builds TUI capture request from provider advertisement", "rejects capture request when the surface does not advertise TUI", "artifacts/evetui-capture/smoke/tui-grid.json"] + "contains": ["builds TUI capture request from provider advertisement", "builds TUI capture request from a declared generic world provider", "rejects capture request when the surface does not advertise TUI", "artifacts/evetui-capture/smoke/tui-grid.json", "eve.world-smoke.commands"] }, { "path": "tools/evetui/evetui-capture-artifact.mjs", @@ -976,7 +988,7 @@ }, { "path": "tools/evetui/evetui-capture-artifact.test.mjs", - "contains": ["builds TUI JSON grid capture artifact", "gamecult.eve.tui_grid.v1", "artifacts/evetui-capture/smoke/tui-grid.json"] + "contains": ["builds TUI JSON grid capture artifact", "builds TUI JSON grid capture artifact from a generic world provider", "gamecult.eve.tui_grid.v1", "artifacts/evetui-capture/smoke/tui-grid.json", "eve.world-smoke.commands"] }, { "path": "tools/evetui/evetui-release-contract.mjs", @@ -999,6 +1011,15 @@ "surfaceId": "aetheria.daemon.game", "sourceSurfacePath": "web/fixtures/aetheria-world-surface.json", "requestPath": "artifacts/evetui-capture/latest/capture-request.json" + }, + { + "kind": "json-grid", + "schema": "gamecult.eve.tui_grid.v1", + "path": "artifacts/evetui-capture/latest/eve-world-smoke-tui-grid.json", + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "sourceSurfacePath": "web/fixtures/eve-world-smoke-surface.json", + "requestPath": "artifacts/evetui-capture/latest/eve-world-smoke-capture-request.json" } ] } @@ -1016,7 +1037,7 @@ "manifestPath": "runtimes/incubating/eve-unity-scene/eve-runtime-capability.json" }, "splitHandoffPath": "runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json", - "requiredFixtures": ["aetheria-world"], + "requiredFixtures": ["aetheria-world", "eve-world-smoke"], "pluginFixtures": ["sai-vn"], "supportedFeatures": ["providerAdvertisements", "commandTransport", "sceneGraphProjection", "embeddedDocuments"], "supportedPlugins": [ @@ -1050,7 +1071,8 @@ "runtimes/incubating/eve-unity-scene/Runtime/NornGraphUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Runtime/TeXMathUnitySceneProjectionAdapter.cs", "runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs", - "web/fixtures/aetheria.provider-advertisement.json" + "web/fixtures/aetheria.provider-advertisement.json", + "web/fixtures/eve-world-smoke.provider-advertisement.json" ] } ], @@ -1100,7 +1122,7 @@ }, { "path": "tools/eveunity/eveunity-capture-contract.test.mjs", - "contains": ["builds Unity scene capture request from provider advertisement", "unity-scene", "artifacts/eveunity-scene-capture/smoke/unity-scene.png"] + "contains": ["builds Unity scene capture request from provider advertisement", "builds Unity UI Toolkit capture request from a declared generic world provider", "builds Unity scene capture request from a declared generic world provider", "unity-scene", "artifacts/eveunity-scene-capture/smoke/unity-scene.png", "eve.world-smoke.commands"] }, { "path": "tools/eveunity/eveunity-scene-capture-artifact.mjs", @@ -1108,7 +1130,7 @@ }, { "path": "tools/eveunity/eveunity-scene-capture-artifact.test.mjs", - "contains": ["builds Unity scene projection capture artifact", "marks Sai required and Norn TeX optional sidecar plugin projections", "gamecult.eve.unity_scene_projection.v1", "unity-scene-projection-json", "optional-nested"] + "contains": ["builds Unity scene projection capture artifact", "builds Unity scene capture artifact for a generic world provider", "marks Sai required and Norn TeX optional sidecar plugin projections", "gamecult.eve.unity_scene_projection.v1", "unity-scene-projection-json", "optional-nested", "eve.world-smoke.commands"] }, { "path": "tools/eveunity/eveunity-release-contract.test.mjs", @@ -1128,6 +1150,16 @@ "surfaceId": "aetheria.daemon.game", "sourceSurfacePath": "web/fixtures/aetheria-world-surface.json", "requestPath": "artifacts/eveunity-scene-capture/latest/capture-request.json" + }, + { + "kind": "json-projection", + "schema": "gamecult.eve.unity_scene_projection.v1", + "path": "artifacts/eveunity-scene-capture/latest/eve-world-smoke-unity-scene-projection.json", + "runtimeId": "unity-scene", + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "sourceSurfacePath": "web/fixtures/eve-world-smoke-surface.json", + "requestPath": "artifacts/eveunity-scene-capture/latest/eve-world-smoke-capture-request.json" } ] } @@ -1521,7 +1553,7 @@ "schemaPath": "schemas/gamecult.eve.runtime_capability.v1.schema.json", "manifestPath": "packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json" }, - "requiredFixtures": ["embedded-surface"], + "requiredFixtures": ["embedded-surface", "eve-world-smoke"], "pluginFixtures": ["sai-vn"], "supportedFeatures": ["embeddedDocuments"], "supportedPlugins": [ @@ -1549,7 +1581,8 @@ "evidencePaths": [ "packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs", "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs", - "web/fixtures/aetheria.provider-advertisement.json" + "web/fixtures/aetheria.provider-advertisement.json", + "web/fixtures/eve-world-smoke.provider-advertisement.json" ] } ], @@ -1599,7 +1632,7 @@ }, { "path": "tools/eveunity/eveunity-uitoolkit-capture-artifact.test.mjs", - "contains": ["builds Unity UI Toolkit projection capture artifact", "marks Sai required and Norn TeX optional sidecar plugin UI projections", "gamecult.eve.unity_uitoolkit_projection.v1", "unity-uitoolkit-projection-json", "optional-nested"] + "contains": ["builds Unity UI Toolkit projection capture artifact", "builds Unity UI Toolkit capture artifact for a generic world provider", "marks Sai required and Norn TeX optional sidecar plugin UI projections", "gamecult.eve.unity_uitoolkit_projection.v1", "unity-uitoolkit-projection-json", "optional-nested", "eve.world-smoke.commands"] } ], "commandTransportSmoke": { @@ -1751,6 +1784,13 @@ "conformanceAttachment": "runtime.captureArtifacts[]", "requiredProvider": "aetheria", "requiredSurface": "aetheria.daemon.game", + "additionalProviderSurfaces": [ + { + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "purpose": "Generic non-product interactive-world capture control." + } + ], "authority": "Capture observes runtime projection only; provider state, command acceptance, and receipts stay provider-owned.", "publishProof": "Unity editor or batchmode capture artifact is attached to the runtime conformance report from EveUnity." }, @@ -1781,6 +1821,16 @@ "surfaceId": "aetheria.daemon.game", "sourceSurfacePath": "web/fixtures/aetheria-world-surface.json", "requestPath": "artifacts/eveunity-uitoolkit-capture/latest/capture-request.json" + }, + { + "kind": "json-projection", + "schema": "gamecult.eve.unity_uitoolkit_projection.v1", + "path": "artifacts/eveunity-uitoolkit-capture/latest/eve-world-smoke-unity-uitoolkit-projection.json", + "runtimeId": "unity-uitoolkit", + "providerId": "eve.world-smoke", + "surfaceId": "eve.world-smoke.surface", + "sourceSurfacePath": "web/fixtures/eve-world-smoke-surface.json", + "requestPath": "artifacts/eveunity-uitoolkit-capture/latest/eve-world-smoke-capture-request.json" } ] } diff --git a/tools/parity/run-parity.mjs b/tools/parity/run-parity.mjs index e87e0d0..a6a5a2a 100644 --- a/tools/parity/run-parity.mjs +++ b/tools/parity/run-parity.mjs @@ -1540,9 +1540,19 @@ function compareCaptureContract(expected, actual, label) { if (actual.requestSchema && !manifest.schemas?.[actual.requestSchema]) { errors.push(`${label}.requestSchema:${actual.requestSchema}:missing-schema-catalog-entry`); } + const expectedAdditionalSurfaces = captureSurfaceClaimKeys(expected.additionalProviderSurfaces || []); + const actualAdditionalSurfaces = captureSurfaceClaimKeys(actual.additionalProviderSurfaces || []); + errors.push(...missingMembers(expectedAdditionalSurfaces, actualAdditionalSurfaces, `${label}.additionalProviderSurfaces`)); return errors; } +function captureSurfaceClaimKeys(claims) { + return (Array.isArray(claims) ? claims : []) + .filter(claim => claim && typeof claim === "object") + .map(claim => `${claim.providerId || ""}:${claim.surfaceId || ""}`) + .filter(claim => claim !== ":"); +} + function compareTestContract(expected, actual, label) { const errors = []; if (!expected) return errors; @@ -2660,6 +2670,7 @@ function buildRuntimeCaptureProbeRecord(runtime) { conformanceAttachment: contract.conformanceAttachment || "", requiredProvider: contract.requiredProvider || "", requiredSurface: contract.requiredSurface || "", + additionalProviderSurfaces: contract.additionalProviderSurfaces || [], authority: contract.authority || "", currentArtifactKind, currentArtifactSchema: artifact.schema || "",