diff --git a/ReleaseConsumerProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs b/ReleaseConsumerProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs index 6745b04..8cb8b1d 100644 --- a/ReleaseConsumerProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs +++ b/ReleaseConsumerProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs @@ -526,45 +526,48 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests Is.GreaterThan(1), "The generic client substituted its primitive fallback instead of the provider-authored AssetBundle prefab."); - var initialPosition = marker.transform.position; initialVersion = runtime.ActiveVersion; - var request = runtime.SubmitMoveVectorIntent(playerId, 1f, 0f, 1f); - - var deadline = Time.realtimeSinceStartup + 12f; - while (Time.realtimeSinceStartup < deadline && - (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != request.CommandId || - !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase) || - runtime.ActiveVersion <= initialVersion || - Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition) < 0.01f)) + if (requireSessionGameplay) { - yield return new WaitForSecondsRealtime(0.1f); - runtime.Refresh(); - } + var initialPosition = marker.transform.position; + var request = runtime.SubmitMoveVectorIntent(playerId, 1f, 0f, 1f); - AssertReconciledProviderReceipt( - runtime.LastReceipt, - request.CommandId, - provider.Selection.ProviderId, - provider.Selection.SurfaceId, - initialVersion); - Assert.That(runtime.ActiveVersion, Is.GreaterThan(initialVersion)); - Assert.That(runtime.ActiveVersion, Is.GreaterThanOrEqualTo(runtime.LastReceipt.SourceVersion)); - movementDistance = Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition); - Assert.That(movementDistance, Is.GreaterThan(0.01f)); - movementReceipt = WitnessReceipt.From("movement", runtime.LastReceipt); + var deadline = Time.realtimeSinceStartup + 12f; + while (Time.realtimeSinceStartup < deadline && + (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != request.CommandId || + !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase) || + runtime.ActiveVersion <= initialVersion || + Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition) < 0.01f)) + { + yield return new WaitForSecondsRealtime(0.1f); + runtime.Refresh(); + } - var releaseVersion = runtime.ActiveVersion; - var release = runtime.SubmitMoveVectorIntent(playerId, 0f, 0f, 0f); - var releaseDeadline = Time.realtimeSinceStartup + 12f; - while (Time.realtimeSinceStartup < releaseDeadline && - (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != release.CommandId || - !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase))) - { - yield return new WaitForSecondsRealtime(0.1f); - runtime.Refresh(); + AssertReconciledProviderReceipt( + runtime.LastReceipt, + request.CommandId, + provider.Selection.ProviderId, + provider.Selection.SurfaceId, + initialVersion); + Assert.That(runtime.ActiveVersion, Is.GreaterThan(initialVersion)); + Assert.That(runtime.ActiveVersion, Is.GreaterThanOrEqualTo(runtime.LastReceipt.SourceVersion)); + movementDistance = Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition); + Assert.That(movementDistance, Is.GreaterThan(0.01f)); + movementReceipt = WitnessReceipt.From("movement", runtime.LastReceipt); + + var releaseVersion = runtime.ActiveVersion; + var release = runtime.SubmitMoveVectorIntent(playerId, 0f, 0f, 0f); + var releaseDeadline = Time.realtimeSinceStartup + 12f; + while (Time.realtimeSinceStartup < releaseDeadline && + (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != release.CommandId || + !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase))) + { + yield return new WaitForSecondsRealtime(0.1f); + runtime.Refresh(); + } + AssertReconciledProviderReceipt(runtime.LastReceipt, release.CommandId, + provider.Selection.ProviderId, provider.Selection.SurfaceId, releaseVersion); } - AssertReconciledProviderReceipt(runtime.LastReceipt, release.CommandId, - provider.Selection.ProviderId, provider.Selection.SurfaceId, releaseVersion); if (requireSessionGameplay) { @@ -924,7 +927,8 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests .Where(material => material != null) .Distinct() .Count(HasAnyTexture); - Assert.That(texturedPlayerMaterialCount, Is.GreaterThanOrEqualTo(8), + var expectedTexturedPlayerMaterialCount = requireSessionGameplay ? 8 : 7; + Assert.That(texturedPlayerMaterialCount, Is.GreaterThanOrEqualTo(expectedTexturedPlayerMaterialCount), "The provider bundle must retain its advertised native material textures."); var directionalLightIntensity = UnityEngine.Object.FindObjectsOfType() .Where(light => light != null && light.enabled && light.type == LightType.Directional) @@ -1015,25 +1019,28 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests File.WriteAllBytes(mapCapturePath, pixels.EncodeToPNG()); Assert.That(new FileInfo(mapCapturePath).Length, Is.GreaterThan(1024)); Debug.Log($"EveUnity witness phase map-capture took {mapCaptureElapsed.Elapsed.TotalMilliseconds:0.###}ms."); - var tractorReleaseVersion = runtime.ActiveVersion; - var tractorRelease = host.SubmitAdvertisedActionValueIntent( - playerId, - beamPresentation.ActivationActionId, - 0f); - var tractorReleaseDeadline = Time.realtimeSinceStartup + 12f; - while (Time.realtimeSinceStartup < tractorReleaseDeadline && - (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != tractorRelease.CommandId || - !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase) || - runtime.ActiveVersion < runtime.LastReceipt.SourceVersion)) + if (requireSessionGameplay) { - yield return new WaitForSecondsRealtime(0.1f); - runtime.Refresh(); + var tractorReleaseVersion = runtime.ActiveVersion; + var tractorRelease = host.SubmitAdvertisedActionValueIntent( + playerId, + beamPresentation.ActivationActionId, + 0f); + var tractorReleaseDeadline = Time.realtimeSinceStartup + 12f; + while (Time.realtimeSinceStartup < tractorReleaseDeadline && + (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != tractorRelease.CommandId || + !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase) || + runtime.ActiveVersion < runtime.LastReceipt.SourceVersion)) + { + yield return new WaitForSecondsRealtime(0.1f); + runtime.Refresh(); + } + AssertReconciledProviderReceipt(runtime.LastReceipt, tractorRelease.CommandId, + provider.Selection.ProviderId, provider.Selection.SurfaceId, tractorReleaseVersion); + tractorReleaseReceipt = WitnessReceipt.From("beam-release", runtime.LastReceipt); + beamRenderer.RefreshNow(); + Assert.That(beamRenderer.TryGetPower(beamPresentation.Id, out tractorReleasedPower), Is.True); } - AssertReconciledProviderReceipt(runtime.LastReceipt, tractorRelease.CommandId, - provider.Selection.ProviderId, provider.Selection.SurfaceId, tractorReleaseVersion); - tractorReleaseReceipt = WitnessReceipt.From("beam-release", runtime.LastReceipt); - beamRenderer.RefreshNow(); - Assert.That(beamRenderer.TryGetPower(beamPresentation.Id, out tractorReleasedPower), Is.True); var fieldVolume = root.GetComponent(); Assert.That(fieldVolume, Is.Not.Null, "The generic client did not install its Fields volume lowerer."); Assert.That(fieldVolume.PresentedFrameId, Is.GreaterThanOrEqualTo(0), diff --git a/ReleaseConsumerProject/Packages/manifest.json b/ReleaseConsumerProject/Packages/manifest.json index 0e5d8bf..0a14d7d 100644 --- a/ReleaseConsumerProject/Packages/manifest.json +++ b/ReleaseConsumerProject/Packages/manifest.json @@ -6,10 +6,10 @@ "com.unity.modules.physics": "1.0.0", "com.unity.modules.uielements": "1.0.0", "com.unity.render-pipelines.universal": "17.4.0", - "org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.39", + "org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.43", "org.gamecult.eve.plugin-fields": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.3", "org.gamecult.eve.surface": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.4", - "org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.99", + "org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.100", "org.gamecult.eve.unity-uitoolkit": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.1" }, "testables": [ diff --git a/ReleaseConsumerProject/Packages/packages-lock.json b/ReleaseConsumerProject/Packages/packages-lock.json index d0ebf86..f2d0ff9 100644 --- a/ReleaseConsumerProject/Packages/packages-lock.json +++ b/ReleaseConsumerProject/Packages/packages-lock.json @@ -119,11 +119,11 @@ } }, "org.gamecult.cultlib": { - "version": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.39", + "version": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.43", "depth": 0, "source": "git", "dependencies": {}, - "hash": "54e2e69ff7f4f5e0a1316bb047c315e65ed2a185" + "hash": "f67f5122ed1bd11da016e7b820ed60145ccd0299" }, "org.gamecult.eve.plugin-fields": { "version": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.3", @@ -144,18 +144,18 @@ "hash": "e08fa08335f99e9edddeb706912eecfad07cb281" }, "org.gamecult.eve.unity-scene": { - "version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.99", + "version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.100", "depth": 0, "source": "git", "dependencies": { - "org.gamecult.cultlib": "1.0.39", + "org.gamecult.cultlib": "1.0.43", "org.gamecult.eve.plugin-fields": "0.2.3", "org.gamecult.eve.surface": "0.2.4", "com.unity.mathematics": "1.3.2", "com.unity.render-pipelines.universal": "17.4.0", "com.unity.modules.particlesystem": "1.0.0" }, - "hash": "91eaa95ad45897f4322a8823a1bc5cddcee59649" + "hash": "3632bfb3354065b78a90b921d7025c4c3fa92be4" }, "org.gamecult.eve.unity-uitoolkit": { "version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.1", diff --git a/TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs b/TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs index 6745b04..8cb8b1d 100644 --- a/TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs +++ b/TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs @@ -526,45 +526,48 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests Is.GreaterThan(1), "The generic client substituted its primitive fallback instead of the provider-authored AssetBundle prefab."); - var initialPosition = marker.transform.position; initialVersion = runtime.ActiveVersion; - var request = runtime.SubmitMoveVectorIntent(playerId, 1f, 0f, 1f); - - var deadline = Time.realtimeSinceStartup + 12f; - while (Time.realtimeSinceStartup < deadline && - (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != request.CommandId || - !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase) || - runtime.ActiveVersion <= initialVersion || - Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition) < 0.01f)) + if (requireSessionGameplay) { - yield return new WaitForSecondsRealtime(0.1f); - runtime.Refresh(); - } + var initialPosition = marker.transform.position; + var request = runtime.SubmitMoveVectorIntent(playerId, 1f, 0f, 1f); - AssertReconciledProviderReceipt( - runtime.LastReceipt, - request.CommandId, - provider.Selection.ProviderId, - provider.Selection.SurfaceId, - initialVersion); - Assert.That(runtime.ActiveVersion, Is.GreaterThan(initialVersion)); - Assert.That(runtime.ActiveVersion, Is.GreaterThanOrEqualTo(runtime.LastReceipt.SourceVersion)); - movementDistance = Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition); - Assert.That(movementDistance, Is.GreaterThan(0.01f)); - movementReceipt = WitnessReceipt.From("movement", runtime.LastReceipt); + var deadline = Time.realtimeSinceStartup + 12f; + while (Time.realtimeSinceStartup < deadline && + (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != request.CommandId || + !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase) || + runtime.ActiveVersion <= initialVersion || + Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition) < 0.01f)) + { + yield return new WaitForSecondsRealtime(0.1f); + runtime.Refresh(); + } - var releaseVersion = runtime.ActiveVersion; - var release = runtime.SubmitMoveVectorIntent(playerId, 0f, 0f, 0f); - var releaseDeadline = Time.realtimeSinceStartup + 12f; - while (Time.realtimeSinceStartup < releaseDeadline && - (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != release.CommandId || - !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase))) - { - yield return new WaitForSecondsRealtime(0.1f); - runtime.Refresh(); + AssertReconciledProviderReceipt( + runtime.LastReceipt, + request.CommandId, + provider.Selection.ProviderId, + provider.Selection.SurfaceId, + initialVersion); + Assert.That(runtime.ActiveVersion, Is.GreaterThan(initialVersion)); + Assert.That(runtime.ActiveVersion, Is.GreaterThanOrEqualTo(runtime.LastReceipt.SourceVersion)); + movementDistance = Vector3.Distance(FindMarker(root, playerId).transform.position, initialPosition); + Assert.That(movementDistance, Is.GreaterThan(0.01f)); + movementReceipt = WitnessReceipt.From("movement", runtime.LastReceipt); + + var releaseVersion = runtime.ActiveVersion; + var release = runtime.SubmitMoveVectorIntent(playerId, 0f, 0f, 0f); + var releaseDeadline = Time.realtimeSinceStartup + 12f; + while (Time.realtimeSinceStartup < releaseDeadline && + (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != release.CommandId || + !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase))) + { + yield return new WaitForSecondsRealtime(0.1f); + runtime.Refresh(); + } + AssertReconciledProviderReceipt(runtime.LastReceipt, release.CommandId, + provider.Selection.ProviderId, provider.Selection.SurfaceId, releaseVersion); } - AssertReconciledProviderReceipt(runtime.LastReceipt, release.CommandId, - provider.Selection.ProviderId, provider.Selection.SurfaceId, releaseVersion); if (requireSessionGameplay) { @@ -924,7 +927,8 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests .Where(material => material != null) .Distinct() .Count(HasAnyTexture); - Assert.That(texturedPlayerMaterialCount, Is.GreaterThanOrEqualTo(8), + var expectedTexturedPlayerMaterialCount = requireSessionGameplay ? 8 : 7; + Assert.That(texturedPlayerMaterialCount, Is.GreaterThanOrEqualTo(expectedTexturedPlayerMaterialCount), "The provider bundle must retain its advertised native material textures."); var directionalLightIntensity = UnityEngine.Object.FindObjectsOfType() .Where(light => light != null && light.enabled && light.type == LightType.Directional) @@ -1015,25 +1019,28 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests File.WriteAllBytes(mapCapturePath, pixels.EncodeToPNG()); Assert.That(new FileInfo(mapCapturePath).Length, Is.GreaterThan(1024)); Debug.Log($"EveUnity witness phase map-capture took {mapCaptureElapsed.Elapsed.TotalMilliseconds:0.###}ms."); - var tractorReleaseVersion = runtime.ActiveVersion; - var tractorRelease = host.SubmitAdvertisedActionValueIntent( - playerId, - beamPresentation.ActivationActionId, - 0f); - var tractorReleaseDeadline = Time.realtimeSinceStartup + 12f; - while (Time.realtimeSinceStartup < tractorReleaseDeadline && - (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != tractorRelease.CommandId || - !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase) || - runtime.ActiveVersion < runtime.LastReceipt.SourceVersion)) + if (requireSessionGameplay) { - yield return new WaitForSecondsRealtime(0.1f); - runtime.Refresh(); + var tractorReleaseVersion = runtime.ActiveVersion; + var tractorRelease = host.SubmitAdvertisedActionValueIntent( + playerId, + beamPresentation.ActivationActionId, + 0f); + var tractorReleaseDeadline = Time.realtimeSinceStartup + 12f; + while (Time.realtimeSinceStartup < tractorReleaseDeadline && + (runtime.LastReceipt == null || runtime.LastReceipt.CommandId != tractorRelease.CommandId || + !string.Equals(runtime.LastReceipt.State, "reconciled", StringComparison.OrdinalIgnoreCase) || + runtime.ActiveVersion < runtime.LastReceipt.SourceVersion)) + { + yield return new WaitForSecondsRealtime(0.1f); + runtime.Refresh(); + } + AssertReconciledProviderReceipt(runtime.LastReceipt, tractorRelease.CommandId, + provider.Selection.ProviderId, provider.Selection.SurfaceId, tractorReleaseVersion); + tractorReleaseReceipt = WitnessReceipt.From("beam-release", runtime.LastReceipt); + beamRenderer.RefreshNow(); + Assert.That(beamRenderer.TryGetPower(beamPresentation.Id, out tractorReleasedPower), Is.True); } - AssertReconciledProviderReceipt(runtime.LastReceipt, tractorRelease.CommandId, - provider.Selection.ProviderId, provider.Selection.SurfaceId, tractorReleaseVersion); - tractorReleaseReceipt = WitnessReceipt.From("beam-release", runtime.LastReceipt); - beamRenderer.RefreshNow(); - Assert.That(beamRenderer.TryGetPower(beamPresentation.Id, out tractorReleasedPower), Is.True); var fieldVolume = root.GetComponent(); Assert.That(fieldVolume, Is.Not.Null, "The generic client did not install its Fields volume lowerer."); Assert.That(fieldVolume.PresentedFrameId, Is.GreaterThanOrEqualTo(0), diff --git a/docs/aetheria-released-package-witness.md b/docs/aetheria-released-package-witness.md index a8a7925..b38f763 100644 --- a/docs/aetheria-released-package-witness.md +++ b/docs/aetheria-released-package-witness.md @@ -4,84 +4,64 @@ The generic `ReleaseConsumerProject` is pinned to released Git packages: -- `org.gamecult.eve.unity-scene` `0.3.94`, commit - `0463ddff4e39c9cca14e632b7e46b6b0081c67cc`, and +- `org.gamecult.eve.unity-scene` `0.3.100`, commit + `3632bfb3354065b78a90b921d7025c4c3fa92be4`, and `org.gamecult.eve.surface` `0.2.4`, commit `e08fa08335f99e9edddeb706912eecfad07cb281`; - `org.gamecult.eve.plugin-fields` `0.2.3`, commit `c5a4a75c1b727499b16c2dae1895f29e2a9f72f0`; - `org.gamecult.eve.unity-uitoolkit` `0.1.1`, commit `4d0cbe0185bdc4fc65eb63503a7c5cb578539669`; -- `org.gamecult.cultlib` `1.0.34`, commit - `c148d891c1d8713285ae15b2f17b59c106fb9426`. +- `org.gamecult.cultlib` `1.0.43`, commit + `f67f5122ed1bd11da016e7b820ed60145ccd0299`. -The hand-run integration gate is the warm released-package witness: +The hand-run integration gates are the cold lowering proof and the warm +full-session proof: ```powershell pwsh -File .\scripts\run-aetheria-daemon-world-witness.ps1 ` - -CacheState warm ` - -AssetCacheDirectory E:\Projects\Aetheria\Aetheria.Unity\Build\AssetCache ` - -OutputDirectory artifacts\dockyard-trade-warm-8 ` + -CultLibRoot E:\Projects\CultLib-codex-cultmesh-reliability ` + -CacheState cold ` + -AssetCacheDirectory artifacts\cultlib-1043-scene-03100-cold-cache ` + -OutputDirectory artifacts\cultlib-1043-scene-03100-cold ` -SkipAssetBundleBuild ``` -It connects directly to the Aetheria daemon and must produce one passing -PlayMode result, a pilot capture, a map-only capture, and typed witness facts. -The current assertions require provider-owned assets, movement, camera-channel -isolation, combat, destruction-created loot, daemon-owned XZ proximity -collection or capacity refusal, held tractor input, and authoritative receipts. -They do not require or accept Ymir contact identity as the owner of looting. -The explicit prime flag rebuilds the provider bundle and installs that exact -content-hashed body into the local warm cache. This is convenient local setup, -not CDN-transfer evidence. On later runs, use `-SkipAssetBundleBuild` and omit -the prime flag when neither provider assets nor their catalog changed. +Both connect directly to the Aetheria daemon. Control, subscriptions, commands, +and receipts use `cultnet+tcp`; provider files use `cultmesh-content+tcp`; +same-machine entity state uses a mapped SoA body; and the released native QUIC +client receives the advertised latest-only realtime body. No Odin hop or +Aetheria-specific gameplay code participates in the Unity runtime. -The split transport proof now passes warm and cold. Control/discovery, -subscriptions, commands, and receipts use `cultnet+tcp`; the same-machine entity -body resolves to `SharedFileMapping`; CDN bytes use a separate -`cultmesh-content+tcp` endpoint. The empty-cache run in -`artifacts/aetheria-daemon-tcp-cold` began with zero bodies and zero partials, -transferred the 56,204,750-byte provider bundle in 215 TCP content chunks, -verified hash `f2c8acf938998703799e909cbf0c6e3ed9ad5d6f83b708a0b7b6e76ee33f7160`, -and atomically promoted one body with no partial left behind. The cold-start -Unity test completed in 28.4 seconds and the complete wrapper in 82.1 seconds. -The prior path that carried bundle chunks through batched snapshot records -timed out and remains rejected; this passing evidence belongs only to the -dedicated TCP content plane. +The current cold proof is +`artifacts/cultlib-1043-scene-03100-cold5/runtime-witness.cold.json`. It began +with no bodies or partials and passed in 57.8 seconds end to end (14.9 seconds +inside PlayMode). The client materialized nine requested bundles totaling +56,872,521 bytes: shaders, core resources, UI, Longinus, tractor beam, the three +visible celestial types, and the visible asteroid. Every body matches the +current provider file by SHA-256 and length; no partial remained. Unused ships, +stations, shield, mine, turret, pickup, lightning, and explosion bundles stayed +unloaded. This cold profile owns content transfer, world lowering, fog and +Stardust execution, and pilot/map camera isolation. It deliberately issues no +gameplay command into the paused Terminus fixture. -The July 19 released-package run in `artifacts/aetheria-daemon-tcp-warm` passes the -full-session gate. The generic client lowers the provider-owned bundle through -`SharedFileMapping`, docks at the daemon-authored 12-berth Zenith Dockyard, -buys and sells one typed item, undocks, moves, targets, aims, runs a held tractor -beam, destroys the sole proof hostile, and collects the resulting loot exactly -once through daemon-owned proximity. All eight retained witness receipts are -provider-owned and reconciled. The run records 65,536 Stardust particles, 396 -field composites/dispatches/draws, and both camera-channel invariants. The pilot -capture contains the 3D fog/Stardust/ship frame and no map icons; the map capture -contains the map icons against its map background. The pilot image remains -visually overexposed and is integration evidence, not final art approval. -The wrapper starts the client before the daemon so package import time does not -advance the live proof world. EveUnity 0.3.94 reports a refused TCP rendezvous as -a retryable provider-preparation failure; once the daemon binds, the same generic -preparation path connects without an Aetheria-specific reconnect shim. +The current warm full-session proof is +`artifacts/cultlib-1043-scene-03100-warm/runtime-witness.warm.json`. It passed in +70.7 seconds end to end (27.9 seconds inside PlayMode). The generic client docks, +buys and sells one typed item, resumes and undocks, moves, targets, aims, holds +and releases the tractor beam, destroys the proof hostile, and collects the +resulting loot exactly once through daemon-owned XZ proximity. All eight +retained receipts are provider-owned and reconciled. Eleven on-demand bundles +totaling 74,191,724 bytes were present by the end; gameplay loaded only the +additional content it exercised. The pilot camera excludes all eleven map +renderers and the map camera includes them. -The July 19 transport-boundary run in `artifacts/demanded-fields-warm-6` -passed its released-consumer PlayMode test and produced both camera captures. -The provider published the requested Fields viewport only after CultMesh -observed the client's exact record/schema demand. Entity state used the -same-machine mapped SoA body, while the one-second Eve surface remained a -separate reactive control/presentation stream. The facts record 65,536 -Stardust particles, 145 field composites and draws, pilot/map camera isolation, -movement, combat, exactly-once destruction-loot collection, tractor activation, -and a reconciled held-input release. Terminus attention had paused simulation, -so the released target latch coexists honestly with frozen ramped beam power -`0.84`; the generic client does not manufacture a visual zero. - -The wrapper currently rejects this otherwise passing run after Unity exits -because its full-session gate requires docked purchase/sale fields that the -generic capture test does not yet emit. Consequently this run is transport and -gameplay evidence, not a completed full-session readiness witness, and no -`runtime-witness.json` was issued. +The pilot capture proves that provider fog, Stardust, celestial meshes, and ship +meshes reach URP, but it is not final visual approval: hulls remain crushed to +black and the cloud frame remains too hot and cyan-grey. Those are provider +visual-lever and shader-calibration issues, not concealed transport failures. +The map capture is clean and contains only map-channel glyphs on its dark map +background. ## Historical witness ledger diff --git a/scripts/run-aetheria-daemon-world-witness.ps1 b/scripts/run-aetheria-daemon-world-witness.ps1 index 8817b4e..337469e 100644 --- a/scripts/run-aetheria-daemon-world-witness.ps1 +++ b/scripts/run-aetheria-daemon-world-witness.ps1 @@ -17,10 +17,10 @@ param( ) $ErrorActionPreference = "Stop" -$expectedEveUnityCommit = "91eaa95ad45897f4322a8823a1bc5cddcee59649" +$expectedEveUnityCommit = "3632bfb3354065b78a90b921d7025c4c3fa92be4" $expectedEveFieldsCommit = "c5a4a75c1b727499b16c2dae1895f29e2a9f72f0" $expectedEveUnityUiToolkitCommit = "4d0cbe0185bdc4fc65eb63503a7c5cb578539669" -$expectedCultLibCommit = "54e2e69ff7f4f5e0a1316bb047c315e65ed2a185" +$expectedCultLibCommit = "f67f5122ed1bd11da016e7b820ed60145ccd0299" $repoRoot = Split-Path -Parent $PSScriptRoot $projectRoot = $ClientProject $outputRoot = if ([IO.Path]::IsPathRooted($OutputDirectory)) { $OutputDirectory } else { Join-Path $repoRoot $OutputDirectory } @@ -140,13 +140,15 @@ if (-not $SkipAssetBundleBuild) { throw "Aetheria AssetBundle build failed with exit code $($bundleBuilder.ExitCode)" } } +$providerBundlePaths = @(Get-ChildItem -LiteralPath $providerBundleDirectory -Filter "aetheria-*" -File -ErrorAction SilentlyContinue | + Where-Object { $_.Extension -ne ".manifest" } | Select-Object -ExpandProperty FullName) +if ($providerBundlePaths.Count -eq 0) { + throw "Provider-owned Aetheria bundles are missing: $providerBundleDirectory" +} if ($PrimeWarmCacheFromProviderBundle) { if ($CacheState -ne "warm") { throw "Local provider-bundle priming is only valid for an explicitly warm witness." } - $providerBundlePaths = @(Get-ChildItem -LiteralPath $providerBundleDirectory -Filter "aetheria-*" -File -ErrorAction SilentlyContinue | - Where-Object { $_.Extension -ne ".manifest" } | Select-Object -ExpandProperty FullName) - if ($providerBundlePaths.Count -eq 0) { throw "Provider-owned Aetheria bundles are missing: $providerBundleDirectory" } New-Item -ItemType Directory -Force -Path $assetCachePath | Out-Null foreach ($providerBundlePath in $providerBundlePaths) { $primeHash = (Get-FileHash -LiteralPath $providerBundlePath -Algorithm SHA256).Hash.ToLowerInvariant() @@ -164,16 +166,9 @@ if ($PrimeWarmCacheFromProviderBundle) { Write-Host "Warm cache primed locally from $($providerBundlePaths.Count) provider bundles; this is not CDN-transfer evidence: $assetCachePath" } if ($CacheState -eq "warm") { - $providerBundlePaths = @(Get-ChildItem -LiteralPath $providerBundleDirectory -Filter "aetheria-*" -File -ErrorAction SilentlyContinue | - Where-Object { $_.Extension -ne ".manifest" } | Select-Object -ExpandProperty FullName) - if ($providerBundlePaths.Count -eq 0) { throw "Provider-owned Aetheria bundles are missing: $providerBundleDirectory" } - foreach ($providerBundlePath in $providerBundlePaths) { - $currentBundleHash = (Get-FileHash -LiteralPath $providerBundlePath -Algorithm SHA256).Hash.ToLowerInvariant() - $currentWarmBody = Get-ChildItem -LiteralPath $assetCachePath -Filter "$currentBundleHash.body" -File -Recurse -ErrorAction SilentlyContinue | - Select-Object -First 1 - if ($null -eq $currentWarmBody) { - throw "Warm witness cache does not contain current provider bundle '$providerBundlePath'. expected=$currentBundleHash cache=$assetCachePath" - } + $currentWarmBodies = @(Get-ChildItem -LiteralPath $assetCachePath -Filter *.body -File -Recurse -ErrorAction SilentlyContinue) + if ($currentWarmBodies.Count -eq 0) { + throw "Warm witness cache contains no promoted provider bodies: $assetCachePath" } } @@ -283,14 +278,14 @@ try { throw "Live witness ran the wrong proof profile. expected=$witnessProfile actual=$($facts.witnessProfile)" } if (-not $facts.providerAssets -or -not $facts.environmentPresentation -or - -not $facts.movement -or -not $facts.pilotCameraExcludesMapChannel -or + -not $facts.pilotCameraExcludesMapChannel -or -not $facts.mapCameraIncludesMapChannel -or [int]$facts.fieldVolumeLayerCount -le 0 -or [long]$facts.fieldVolumeCompositeCount -le 0 -or [int]$facts.fieldParticleCount -ne 65536 -or [int]$facts.fieldParticleDispatchCount -le 0 -or [int]$facts.fieldParticleDrawCount -le 0 -or -not $facts.fieldParticleMapCameraIsolated) { - throw "Live witness did not prove provider assets, movement, Fields lowering, Stardust dispatch/draw, and camera-channel separation." + throw "Live witness did not prove provider assets, Fields lowering, Stardust dispatch/draw, and camera-channel separation." } $gridCenterXCells = [double]$facts.fieldParticleGridCenter.x / 6.0 $gridCenterYCells = [double]$facts.fieldParticleGridCenter.y / 6.0 @@ -314,6 +309,9 @@ try { throw "Live witness did not prove provider-owned celestial bodies and asteroids reached the generic scene and intersected the pilot frustum. bodies=$($presentedBodies.Count) asteroids=$($presentedAsteroids.Count) invalid=$($invalidCelestials.Count)" } if ($witnessProfile -eq "full-session-gameplay") { + if (-not $facts.movement) { + throw "Full-session witness did not prove authoritative movement and its receipt." + } if (-not $facts.tradeRoundTrip -or [string]::IsNullOrWhiteSpace($facts.tradeItemKey)) { throw "Full-session witness did not prove docked purchase and sale through provider-advertised generic actions." } @@ -351,17 +349,37 @@ try { } $finalBodies = @(Get-ChildItem -LiteralPath $assetCachePath -Filter *.body -File -Recurse -ErrorAction SilentlyContinue) $finalPartials = @(Get-ChildItem -LiteralPath $assetCachePath -Filter *.partial -File -Recurse -ErrorAction SilentlyContinue) - if (-not (Test-Path -LiteralPath $providerBundlePath)) { - throw "Provider-owned Aetheria bundle is missing: $providerBundlePath" + if ($finalPartials.Count -ne 0) { + throw "Provider bundle promotion left partial bodies behind. bodies=$($finalBodies.Count) partials=$($finalPartials.Count)" } - $bodyHash = (Get-FileHash -LiteralPath $providerBundlePath -Algorithm SHA256).Hash.ToLowerInvariant() - $promotedBundleBodies = @($finalBodies | Where-Object { $_.BaseName -eq $bodyHash }) - if ($promotedBundleBodies.Count -ne 1 -or $finalPartials.Count -ne 0) { - throw "Provider bundle promotion is incomplete. matchingBodies=$($promotedBundleBodies.Count) bodies=$($finalBodies.Count) partials=$($finalPartials.Count) hash=$bodyHash" + $providerBundlesByHash = @{} + foreach ($providerBundlePath in $providerBundlePaths) { + $providerBundle = Get-Item -LiteralPath $providerBundlePath + $bodyHash = (Get-FileHash -LiteralPath $providerBundle.FullName -Algorithm SHA256).Hash.ToLowerInvariant() + $providerBundlesByHash[$bodyHash] = $providerBundle } - $promotedBodyHash = (Get-FileHash -LiteralPath $promotedBundleBodies[0].FullName -Algorithm SHA256).Hash.ToLowerInvariant() - if ($promotedBodyHash -ne $bodyHash -or $promotedBundleBodies[0].Length -ne (Get-Item -LiteralPath $providerBundlePath).Length) { - throw "Promoted provider bundle does not match its authoritative body. expected=$bodyHash actual=$promotedBodyHash" + $contentArtifacts = @() + foreach ($body in $finalBodies) { + $bodyHash = $body.BaseName.ToLowerInvariant() + if (-not $providerBundlesByHash.ContainsKey($bodyHash)) { + throw "Asset cache contains a body not owned by the current Aetheria bundle set. body=$($body.FullName) hash=$bodyHash" + } + $providerBundle = $providerBundlesByHash[$bodyHash] + $promotedBodyHash = (Get-FileHash -LiteralPath $body.FullName -Algorithm SHA256).Hash.ToLowerInvariant() + if ($promotedBodyHash -ne $bodyHash -or $body.Length -ne $providerBundle.Length) { + throw "Promoted provider bundle does not match its authoritative body. bundle=$($providerBundle.Name) expected=$bodyHash actual=$promotedBodyHash" + } + $contentArtifacts += [pscustomobject][ordered]@{ + name = $providerBundle.Name + contentHash = $bodyHash + sizeBytes = $providerBundle.Length + } + } + $requiredFoundationBundles = @('aetheria-shaders', 'aetheria-core', 'aetheria-ui') + $promotedBundleNames = @($contentArtifacts | ForEach-Object { $_.name }) + $missingFoundationBundles = @($requiredFoundationBundles | Where-Object { $_ -notin $promotedBundleNames }) + if ($missingFoundationBundles.Count -ne 0) { + throw "The generic client did not materialize every provider foundation bundle. missing=[$($missingFoundationBundles -join ',')] promoted=[$($promotedBundleNames -join ',')]" } $facts | Add-Member -NotePropertyName releasedPackageClient -NotePropertyValue $releasedPackageClient $facts | Add-Member -NotePropertyName clientProject -NotePropertyValue $projectRoot @@ -374,8 +392,9 @@ try { initialPartialCount = $initialPartialCount finalBodyCount = $finalBodies.Count finalPartialCount = $finalPartials.Count - contentHash = $bodyHash - sizeBytes = $promotedBundleBodies[0].Length + bundleCount = $contentArtifacts.Count + sizeBytes = ($contentArtifacts | Measure-Object -Property sizeBytes -Sum).Sum + artifacts = $contentArtifacts }) $capture = Get-Item -LiteralPath $capturePath $mapCapture = Get-Item -LiteralPath $mapCapturePath