diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 204a15c..d8ec688 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -428,9 +428,11 @@ Recently cut: `scripts/run-eveunity-scene-release-contract-smoke.ps1`. It also reuses the shared EveUnity capture request builder through `scripts/run-eveunity-scene-capture-contract-smoke.ps1`, while the actual - Unity scene PNG/frame artifact remains pending. Plugin projection adapters - stay unsupported until Sai, Norn, or TeX sidecar projection support exists in - the Unity scene runtime. + Unity scene PNG/frame artifact remains pending. The scene graph now preserves + embedded document slot identity for resolver-backed child surfaces without + owning the nested plugin semantics. Plugin projection adapters stay + unsupported until Sai, Norn, or TeX sidecar projection support exists in the + Unity scene runtime. - Unity scene now has a direct split handoff smoke at `scripts/run-eveunity-scene-split-handoff-smoke.ps1`. The smoke verifies the scene runtime's current source paths for runtime body, scene graph lowering, diff --git a/docs/parity-testing-harness.md b/docs/parity-testing-harness.md index acc203e..f2d782e 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -331,9 +331,9 @@ Plugin projection and repo graduation remain blockers. EveUnity includes both the active `unity-uitoolkit` package proof and the pending `unity-scene` runtime boundary; UI Toolkit support is not treated as full Unity scene/world lowering. The `unity-scene` runtime now claims -a narrow scene graph command surface and exposes a typed capture request -contract, while actual scene capture, release, and plugin projection adapters -remain explicit blockers. +a narrow scene graph command surface, preserves embedded document slot identity, +and exposes a typed capture request contract, while actual scene capture, +release, and plugin projection adapters remain explicit blockers. The Flutter widget smoke also exercises `EveProviderCatalog` and `EveProviderPicker` against a conformance-export-shaped provider list. That is diff --git a/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs b/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs index 6c0207d..933bfcb 100644 --- a/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs +++ b/runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs @@ -84,9 +84,25 @@ namespace GameCult.Eve.UnityScene component.Style, component.StateBindings.Count, component.EmbeddedDocuments.Count, + BuildEmbeddedDocumentSlots(component.EmbeddedDocuments), children); } + private static IReadOnlyList BuildEmbeddedDocumentSlots( + IReadOnlyList embeddedDocuments) + { + var slots = new List(embeddedDocuments.Count); + foreach (var slot in embeddedDocuments) + { + slots.Add(new EveUnitySceneEmbeddedDocumentSlot( + slot.SlotId, + slot.DocumentId, + slot.SchemaId, + slot.PresentationKind)); + } + return slots; + } + private static string SceneObjectKind(string componentKind) { if (string.IsNullOrWhiteSpace(componentKind)) @@ -151,6 +167,7 @@ namespace GameCult.Eve.UnityScene IReadOnlyDictionary style, int stateBindingCount, int embeddedDocumentCount, + IReadOnlyList embeddedDocuments, IReadOnlyList children) { Id = id ?? ""; @@ -161,6 +178,7 @@ namespace GameCult.Eve.UnityScene Style = style ?? new Dictionary(StringComparer.Ordinal); StateBindingCount = stateBindingCount; EmbeddedDocumentCount = embeddedDocumentCount; + EmbeddedDocuments = embeddedDocuments ?? Array.Empty(); Children = children ?? Array.Empty(); } @@ -180,9 +198,34 @@ namespace GameCult.Eve.UnityScene public int EmbeddedDocumentCount { get; } + public IReadOnlyList EmbeddedDocuments { get; } + public IReadOnlyList Children { get; } } + public sealed class EveUnitySceneEmbeddedDocumentSlot + { + public EveUnitySceneEmbeddedDocumentSlot( + string slotId, + string documentId, + string schemaId, + string presentationKind) + { + SlotId = slotId ?? ""; + DocumentId = documentId ?? ""; + SchemaId = schemaId ?? ""; + PresentationKind = presentationKind ?? ""; + } + + public string SlotId { get; } + + public string DocumentId { get; } + + public string SchemaId { get; } + + public string PresentationKind { get; } + } + public sealed class EveUnitySceneProviderSurfaceAdvertisement { public EveUnitySceneProviderSurfaceAdvertisement( diff --git a/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs b/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs index a3e69e6..a64e435 100644 --- a/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs +++ b/runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs @@ -40,6 +40,11 @@ namespace GameCult.Eve.UnityScene.Tests Assert.That(projection.Root.Children[1].Props["command"], Is.EqualTo("aetheria.daemon.focus")); Assert.That(projection.Root.Children[2].SceneObjectKind, Is.EqualTo("plugin-placeholder")); Assert.That(projection.Root.Children[2].EmbeddedDocumentCount, Is.EqualTo(1)); + Assert.That(projection.Root.Children[2].EmbeddedDocuments.Count, Is.EqualTo(1)); + Assert.That(projection.Root.Children[2].EmbeddedDocuments[0].SlotId, Is.EqualTo("norn.map")); + Assert.That(projection.Root.Children[2].EmbeddedDocuments[0].DocumentId, Is.EqualTo("cultmesh://aetheria/norn/map")); + Assert.That(projection.Root.Children[2].EmbeddedDocuments[0].SchemaId, Is.EqualTo("gamecult.eve.surface.v1")); + Assert.That(projection.Root.Children[2].EmbeddedDocuments[0].PresentationKind, Is.EqualTo("scene-overlay")); } [Test] diff --git a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json index 8adcb80..4793034 100644 --- a/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json +++ b/runtimes/incubating/eve-unity-scene/eve-runtime-capability.json @@ -16,7 +16,8 @@ "supportedFeatures": [ "providerAdvertisements", "commandTransport", - "sceneGraphProjection" + "sceneGraphProjection", + "embeddedDocuments" ], "supportedPlugins": [], "unsupportedPlugins": [ diff --git a/scripts/run-eveunity-scene-lifecycle-smoke.ps1 b/scripts/run-eveunity-scene-lifecycle-smoke.ps1 index 549bcd2..46c753c 100644 --- a/scripts/run-eveunity-scene-lifecycle-smoke.ps1 +++ b/scripts/run-eveunity-scene-lifecycle-smoke.ps1 @@ -39,7 +39,7 @@ if ($manifest.incubation.splitHandoff.manifestPath -ne "runtimes/incubating/eve- throw "EveUnity scene manifest missing split handoff path" } -foreach ($feature in @("providerAdvertisements", "commandTransport", "sceneGraphProjection")) { +foreach ($feature in @("providerAdvertisements", "commandTransport", "sceneGraphProjection", "embeddedDocuments")) { if (-not (@($manifest.supportedFeatures) -contains $feature)) { throw "EveUnity scene manifest missing provider-shell feature: $feature" } diff --git a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 index 5265eba..a07ae78 100644 --- a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 +++ b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 @@ -67,6 +67,7 @@ node $consumerScript $consumerExport ` --expect-runtime-feature electron-shell:commandTransport ` --expect-runtime-feature electron-shell:surfaceTreeProjection ` --expect-runtime-feature electron-shell:embeddedDocuments ` + --expect-runtime-feature unity-scene:embeddedDocuments ` --expect-runtime-feature tui:providerAdvertisements ` --expect-runtime-feature tui:commandTransport ` --expect-runtime-feature tui:terminalGridSummary ` diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index f5cbb24..947d96d 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -942,7 +942,7 @@ "splitHandoffPath": "runtimes/incubating/eve-unity-scene/eveunity-scene-split-handoff.json", "requiredFixtures": ["aetheria-world"], "pluginFixtures": [], - "supportedFeatures": ["providerAdvertisements", "commandTransport", "sceneGraphProjection"], + "supportedFeatures": ["providerAdvertisements", "commandTransport", "sceneGraphProjection", "embeddedDocuments"], "supportedPlugins": [], "unsupportedPlugins": [ { @@ -992,11 +992,11 @@ "expectedSourceSymbols": [ { "path": "runtimes/incubating/eve-unity-scene/Runtime/EveUnitySceneSurfaceLowerer.cs", - "contains": ["EveUnitySceneSurfaceLowerer", "EveUnitySceneProviderSurfaceAdvertisement", "WorldInteraction", "CommandBoundary", "ReceiptSchema", "EveSurfaceCommandRequest", "BuildSceneGraph", "EveUnitySceneNode", "SceneObjectKind", "unity-scene"] + "contains": ["EveUnitySceneSurfaceLowerer", "EveUnitySceneProviderSurfaceAdvertisement", "WorldInteraction", "CommandBoundary", "ReceiptSchema", "EveSurfaceCommandRequest", "BuildSceneGraph", "BuildEmbeddedDocumentSlots", "EveUnitySceneEmbeddedDocumentSlot", "EveUnitySceneNode", "SceneObjectKind", "unity-scene"] }, { "path": "runtimes/incubating/eve-unity-scene/Tests/Editor/EveUnitySceneSurfaceLowererTests.cs", - "contains": ["LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "plugin-placeholder", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1"] + "contains": ["LowerCarriesProviderAdvertisedWorldBoundary", "LowerBuildsProviderAgnosticSceneGraphFromSurfaceTree", "CommandIntentCarriesAdvertisedBoundaryWithoutOwningReceipts", "world-projection-node", "plugin-placeholder", "norn.map", "cultmesh://aetheria/norn/map", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1"] }, { "path": "tools/eveunity/eveunity-capture-contract.test.mjs",