Prove released split-bundle Aetheria sessions

This commit is contained in:
Meta Vens 2026-07-20 04:18:13 +01:00
commit 45b828d5b3
6 changed files with 219 additions and 206 deletions

View file

@ -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<Light>()
.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<EveUnityFieldsVolumeRenderer>();
Assert.That(fieldVolume, Is.Not.Null, "The generic client did not install its Fields volume lowerer.");
Assert.That(fieldVolume.PresentedFrameId, Is.GreaterThanOrEqualTo(0),

View file

@ -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": [

View file

@ -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",

View file

@ -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<Light>()
.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<EveUnityFieldsVolumeRenderer>();
Assert.That(fieldVolume, Is.Not.Null, "The generic client did not install its Fields volume lowerer.");
Assert.That(fieldVolume.PresentedFrameId, Is.GreaterThanOrEqualTo(0),

View file

@ -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

View file

@ -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