diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index 2469181a6..ef14b14c2 100644 Binary files a/.voidbot/state/aetheria.cc and b/.voidbot/state/aetheria.cc differ diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index a6939c17f..5968585d6 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -9,6 +9,7 @@ var statePath = args.Length > 1 RequireGameplaySourcePurity(root); RequirePackageSerializerBoundary(root); +RequireEveRuntimeBootstrap(root); await using var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-state-verify"); @@ -447,6 +448,7 @@ Console.WriteLine($"Corporation allegiance edges: {corporationAllegianceEdges}") Console.WriteLine($"Name files: {nameFiles.Length}"); Console.WriteLine("Live gameplay source purity: no serializer or legacy database symbols in Assets/Scripts"); Console.WriteLine("Package serializer boundary: MessagePack symbols remain in named CultCache transport files only"); +Console.WriteLine("Eve runtime bootstrap: operations surface mounts through UI Toolkit presenter"); static void RequireCount(AetheriaMigrationLedger ledger, string documentType, int actual) { @@ -549,6 +551,49 @@ static void RequirePackageSerializerBoundary(string root) } } +static void RequireEveRuntimeBootstrap(string root) +{ + var bootstrapPath = Path.Combine( + root, + "Packages", + "org.gamecult.aetheria.eve-runtime", + "Runtime", + "AetheriaEveRuntimeBootstrap.cs"); + var presenterPath = Path.Combine( + root, + "Packages", + "org.gamecult.aetheria.eve-runtime", + "Runtime", + "AetheriaEveSurfacePresenter.cs"); + + if (!File.Exists(bootstrapPath)) + { + throw new InvalidOperationException("Aetheria Eve runtime bootstrap is missing."); + } + + if (!File.Exists(presenterPath)) + { + throw new InvalidOperationException("Aetheria Eve surface presenter is missing."); + } + + var bootstrap = File.ReadAllText(bootstrapPath); + if (!bootstrap.Contains("RuntimeInitializeOnLoadMethod", StringComparison.Ordinal) || + !bootstrap.Contains("DefaultSurfaceId = \"aetheria.operations\"", StringComparison.Ordinal) || + !bootstrap.Contains("AetheriaEveSurfacePresenter", StringComparison.Ordinal) || + !bootstrap.Contains("UIDocument", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Aetheria Eve runtime bootstrap no longer mounts the operations surface through the UI Toolkit presenter."); + } + + var presenter = File.ReadAllText(presenterPath); + if (!presenter.Contains("AetheriaRuntimeEveCommandLog.QueueCommand", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Aetheria Eve presenter no longer queues renderer commands through the typed Eve command log."); + } +} + static bool ContainsBehaviorValueKind(AetheriaBehaviorValue value, string kind) { return string.Equals(value.Kind, kind, StringComparison.OrdinalIgnoreCase) || diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index a060541bb..ac4c85456 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -55,6 +55,10 @@ provider-owned acceptance organ for those commands: it validates the provider, surface, and command template before running the narrow refresh handlers for the catalog and operations surfaces. Renderer callbacks do not accept commands or mutate game state locally. +`AetheriaEveRuntimeBootstrap` mounts the `aetheria.operations` surface through a +runtime-created `UIDocument` after scene load, with environment and command-line +switches for diagnostics. UI Toolkit is now a live runtime lowering path, not +only a package sample. The old `SavedGame`/`SavedZone` DTOs and `Galaxy` loader constructor have also been deleted; the new document family is live Verse run/zone state, not a bespoke save-file format. @@ -288,7 +292,9 @@ named package/import CultCache transport boundary, not gameplay state authority. The package boundary is also fenced: MessagePack symbols may only appear in the runtime catalog reader, pending CultCache envelope store, and typed runtime/Eve command document files until generated CultCache serializers replace that -temporary Unity-side codec. +temporary Unity-side codec. The verifier also checks that the Eve runtime +bootstrap exists, mounts the operations surface through the UI Toolkit presenter, +and keeps renderer commands routed through the typed Eve command log. Use it after import when `GameData/aetheria-world.cc` changes. `Aetheria.State.Unity.Smoke` opens the materialized state through the runtime diff --git a/Packages/org.gamecult.aetheria.eve-runtime/README.md b/Packages/org.gamecult.aetheria.eve-runtime/README.md index 0078d0a58..3bbc7aa66 100644 --- a/Packages/org.gamecult.aetheria.eve-runtime/README.md +++ b/Packages/org.gamecult.aetheria.eve-runtime/README.md @@ -10,6 +10,15 @@ CultCache state into Unity UI Toolkit. - it queues renderer-emitted commands as typed `.cc.eve.pending` command envelopes for Aetheria's provider-owned command bridge. +`AetheriaEveRuntimeBootstrap` mounts the first runtime surface after scene load. +By default it creates a `UIDocument` host for `aetheria.operations`, so the +provider-owned operations surface is present at runtime without a hand-wired +scene object. Set `AETHERIA_EVE_SURFACE_ID` to mount a different surface, set +`AETHERIA_EVE_STATE_PATH` to point at a non-default `.cc` state file, or disable +the automatic mount with `AETHERIA_DISABLE_EVE_RUNTIME_BOOTSTRAP=true` or +`--aetheria-disable-eve-runtime-bootstrap`. Batchmode disables the bootstrap so +compile and smoke gates do not accidentally create renderer state. + Commands are surfaced as `gamecult.eve.command.v1` requests. The presenter does not accept or apply them locally; provider acceptance still belongs to the CultMesh command bridge. Renderer-local command effects would be the wrong diff --git a/Packages/org.gamecult.aetheria.eve-runtime/Runtime/AetheriaEveRuntimeBootstrap.cs b/Packages/org.gamecult.aetheria.eve-runtime/Runtime/AetheriaEveRuntimeBootstrap.cs new file mode 100644 index 000000000..1252d4838 --- /dev/null +++ b/Packages/org.gamecult.aetheria.eve-runtime/Runtime/AetheriaEveRuntimeBootstrap.cs @@ -0,0 +1,60 @@ +using System; +using System.Linq; +using UnityEngine; +using UnityEngine.UIElements; + +#nullable enable + +namespace GameCult.Aetheria.EveRuntime +{ + public static class AetheriaEveRuntimeBootstrap + { + public const string DefaultSurfaceId = "aetheria.operations"; + public const string DisableEnvironmentVariable = "AETHERIA_DISABLE_EVE_RUNTIME_BOOTSTRAP"; + public const string SurfaceEnvironmentVariable = "AETHERIA_EVE_SURFACE_ID"; + public const string StatePathEnvironmentVariable = "AETHERIA_EVE_STATE_PATH"; + public const string DisableCommandLineSwitch = "--aetheria-disable-eve-runtime-bootstrap"; + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + private static void MountDefaultSurface() + { + if (IsDisabled() || UnityEngine.Object.FindObjectOfType() != null) + return; + + var host = new GameObject("Aetheria Eve Runtime Surface"); + UnityEngine.Object.DontDestroyOnLoad(host); + + host.AddComponent(); + var presenter = host.AddComponent(); + presenter.SurfaceId = SurfaceId(); + presenter.StateFilePathOverride = StatePathOverride(); + } + + private static bool IsDisabled() + { + if (Application.isBatchMode) + return true; + + var environment = Environment.GetEnvironmentVariable(DisableEnvironmentVariable); + if (string.Equals(environment, "1", StringComparison.OrdinalIgnoreCase) || + string.Equals(environment, "true", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return Environment.GetCommandLineArgs() + .Any(argument => string.Equals(argument, DisableCommandLineSwitch, StringComparison.OrdinalIgnoreCase)); + } + + private static string SurfaceId() + { + var configured = Environment.GetEnvironmentVariable(SurfaceEnvironmentVariable); + return string.IsNullOrWhiteSpace(configured) ? DefaultSurfaceId : configured; + } + + private static string StatePathOverride() + { + return Environment.GetEnvironmentVariable(StatePathEnvironmentVariable) ?? ""; + } + } +} diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 096051dba..ef2d6b99d 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -497,6 +497,11 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. surface lookup, lowering, and command emission into a typed pending queue. Provider command acceptance still belongs to the future CultMesh command bridge. +- `AetheriaEveRuntimeBootstrap` mounts the first runtime Eve surface after + scene load. The default surface is `aetheria.operations`, hosted in a + runtime-created `UIDocument`; environment variables or a command-line switch + can redirect/disable the mount for diagnostics. Batchmode disables the + bootstrap so verification runs do not create renderer state. - `GameData/aetheria-world.cc` is now materialized from the importer as the project-local typed state file for the checked-in catalog. The importer stores relative provenance in the state document, not machine-local absolute paths. @@ -1341,8 +1346,9 @@ First Aetheria surfaces to publish: serializers directly. - Extend the command bridge beyond refresh commands as gameplay/editor Eve surfaces acquire provider-owned handlers. - - Wire the presenter into a Unity scene/prefab for the first runtime surface - and replace a concrete uGUI screen. + - Done: wire the presenter into runtime through `AetheriaEveRuntimeBootstrap` + so the operations surface mounts as a UI Toolkit surface after scene load. + - Replace a concrete uGUI screen with an Eve-owned surface. - Move the staged packages into the Eve repo once its worktree is clean, then import them back into Aetheria from Eve instead of carrying a local copy. - Replace the old IMGUI DB inspector first, because it is closest to state