From 2f05a81517190ec29464c12834aef73a3b1168e7 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 17 Jul 2026 13:57:34 +0100 Subject: [PATCH] Prove released Aetheria gameplay witness --- .../PlayMode/GenericWorldCaptureTests.cs | 35 +++++++---- ReleaseConsumerProject/Packages/manifest.json | 2 +- .../Packages/packages-lock.json | 4 +- .../PlayMode/GenericWorldCaptureTests.cs | 35 +++++++---- docs/aetheria-released-package-witness.md | 61 ++++++++++++++++--- scripts/run-aetheria-daemon-world-witness.ps1 | 50 +++++++++++---- 6 files changed, 141 insertions(+), 46 deletions(-) diff --git a/ReleaseConsumerProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs b/ReleaseConsumerProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs index 152bac1..4d05854 100644 --- a/ReleaseConsumerProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs +++ b/ReleaseConsumerProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs @@ -522,7 +522,13 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests var inputDriver = root.GetComponent() ?? root.AddComponent(); inputDriver.Host = host; - var action = inputDriver.SubmitPrimaryAction(); + var primaryActionId = EveUnityPlayableWorldInputDriver.ResolvePrimaryActionId(host.InputCapability); + Assert.That(primaryActionId, Is.Not.Empty, + "The advertised input capability did not bind a primary action for this generic client."); + var primaryAction = EveUnityAdvertisedInputAction.Resolve(host.InputCapability, primaryActionId); + Assert.That(primaryAction.IsButtonHold, Is.True, + "The witness primary action must be driven through its advertised held-input semantics."); + var action = inputDriver.SubmitActionValue(primaryActionId, 1f); Assert.That(action, Is.Not.Null, "The advertised input capability did not bind a primary action for this generic client."); var actionDeadline = Time.realtimeSinceStartup + 12f; @@ -569,6 +575,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests "Daemon destruction did not publish its guaranteed cargo as a pickup."); var collectionDeadline = Time.realtimeSinceStartup + 15f; var nextTractorAimAt = 0f; + var salvageApproachActive = false; while (Time.realtimeSinceStartup < collectionDeadline && (expectCargoRejection ? pickupRejection == null : pickupCollection == null)) { @@ -589,23 +596,27 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests continue; tractorAim.Normalize(); runtime.SubmitLookDirectionIntent(playerId, tractorAim.x, 0f, tractorAim.z); + runtime.SubmitMoveVectorIntent(playerId, 0f, 1f, 1f); + salvageApproachActive = true; nextTractorAimAt = Time.realtimeSinceStartup + 0.25f; } + if (salvageApproachActive) + runtime.SubmitMoveVectorIntent(playerId, 0f, 0f, 0f); finalPickupEntityCount = host.PresentedEntities.CurrentGeneration.Entities.Count(entity => string.Equals(entity.EntityKind, "pickup", StringComparison.Ordinal)); if (expectCargoRejection) { Assert.That(pickupRejection, Is.Not.Null, - "Full cargo did not reject destruction salvage from its Ymir contact fact."); + "Full cargo did not reject destruction salvage at daemon pickup proximity."); Assert.That(pickupRejectionEventCount, Is.EqualTo(1), - "The first destruction-loot Ymir contact transaction must emit one rejection event."); + "The first destruction-loot proximity attempt must emit one rejection event."); Assert.That(pickupRejectionEventIds.Count, Is.EqualTo(pickupRejectionEventCount), - "One Ymir fact identity emitted duplicate player rejection feedback."); + "One daemon proximity attempt emitted duplicate player rejection feedback."); Assert.That(pickupCollectionEventCount, Is.Zero, "Capacity-rejected salvage was also collected."); Assert.That(pickupRejection.ItemKey, Is.Not.Empty); - Assert.That(pickupRejection.EventId, Does.StartWith("ymir-fact:"), - "Rejection feedback must retain the consumed Ymir fact identity."); + Assert.That(pickupRejection.EventId, Does.StartWith("frame:"), + "Capacity feedback must retain its daemon frame/zone/pickup attempt identity."); Assert.That(pickupRejection.Reason, Is.EqualTo("cargo-capacity")); Assert.That(pickupRejection.ScalarValue, Is.EqualTo(1)); Assert.That(pickupRejection.CargoQuantityAfter, @@ -616,15 +627,15 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests else { Assert.That(pickupCollection, Is.Not.Null, - "Destruction-created salvage never reached cargo through a Ymir contact fact."); + "Destruction-created salvage never reached cargo through daemon pickup proximity."); Assert.That(pickupCollectionEventCount, Is.EqualTo(1), - "One destruction-loot Ymir contact transaction must emit one collection event."); + "One durable destruction pickup must emit one collection event."); Assert.That(pickupRejectionEventCount, Is.Zero, "Collectible destruction salvage was also rejected."); Assert.That(pickupCollection.ItemKey, Is.Not.Empty, "Destruction loot must retain its canonical typed item identity."); - Assert.That(pickupCollection.EventId, Does.StartWith("ymir-fact:"), - "Collection feedback must retain the consumed Ymir fact identity."); + Assert.That(pickupCollection.EventId, Does.StartWith("zone:"), + "Collection feedback must retain its stable daemon zone/pickup identity."); Assert.That(pickupCollection.ScalarValue, Is.EqualTo(1)); Assert.That(pickupCollection.CargoQuantityBefore, Is.Zero); Assert.That(pickupCollection.CargoQuantityAfter, Is.EqualTo(1)); @@ -1127,8 +1138,8 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests pickupCollectionEventCount == 0 && string.Equals(pickupRejection.Reason, "cargo-capacity", StringComparison.Ordinal) && pickupRejection.CargoQuantityAfter == pickupRejection.CargoQuantityBefore, - destructionLoot = (pickupCollection?.EventId ?? pickupRejection?.EventId ?? "") - .StartsWith("ymir-fact:", StringComparison.Ordinal), + destructionLoot = pickupEvent != null && !string.IsNullOrWhiteSpace(pickupEvent.EventId) && + !string.IsNullOrWhiteSpace(pickupEvent.ItemKey) && pickupEvent.ScalarValue > 0, initialPickupEntityCount = initialPickupEntityCount, finalPickupEntityCount = finalPickupEntityCount, pickupCollectionEventCount = pickupCollectionEventCount, diff --git a/ReleaseConsumerProject/Packages/manifest.json b/ReleaseConsumerProject/Packages/manifest.json index 4b37430..89378f5 100644 --- a/ReleaseConsumerProject/Packages/manifest.json +++ b/ReleaseConsumerProject/Packages/manifest.json @@ -6,7 +6,7 @@ "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.15", + "org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.16", "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.65", diff --git a/ReleaseConsumerProject/Packages/packages-lock.json b/ReleaseConsumerProject/Packages/packages-lock.json index 4b4c386..79b85ec 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.15", + "version": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.16", "depth": 0, "source": "git", "dependencies": {}, - "hash": "419053ebe2325848051c4f4d8ba352cd4286c424" + "hash": "69caf2d027c49227e62ffa24452fc7d717f86ca5" }, "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", diff --git a/TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs b/TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs index 152bac1..4d05854 100644 --- a/TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs +++ b/TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs @@ -522,7 +522,13 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests var inputDriver = root.GetComponent() ?? root.AddComponent(); inputDriver.Host = host; - var action = inputDriver.SubmitPrimaryAction(); + var primaryActionId = EveUnityPlayableWorldInputDriver.ResolvePrimaryActionId(host.InputCapability); + Assert.That(primaryActionId, Is.Not.Empty, + "The advertised input capability did not bind a primary action for this generic client."); + var primaryAction = EveUnityAdvertisedInputAction.Resolve(host.InputCapability, primaryActionId); + Assert.That(primaryAction.IsButtonHold, Is.True, + "The witness primary action must be driven through its advertised held-input semantics."); + var action = inputDriver.SubmitActionValue(primaryActionId, 1f); Assert.That(action, Is.Not.Null, "The advertised input capability did not bind a primary action for this generic client."); var actionDeadline = Time.realtimeSinceStartup + 12f; @@ -569,6 +575,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests "Daemon destruction did not publish its guaranteed cargo as a pickup."); var collectionDeadline = Time.realtimeSinceStartup + 15f; var nextTractorAimAt = 0f; + var salvageApproachActive = false; while (Time.realtimeSinceStartup < collectionDeadline && (expectCargoRejection ? pickupRejection == null : pickupCollection == null)) { @@ -589,23 +596,27 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests continue; tractorAim.Normalize(); runtime.SubmitLookDirectionIntent(playerId, tractorAim.x, 0f, tractorAim.z); + runtime.SubmitMoveVectorIntent(playerId, 0f, 1f, 1f); + salvageApproachActive = true; nextTractorAimAt = Time.realtimeSinceStartup + 0.25f; } + if (salvageApproachActive) + runtime.SubmitMoveVectorIntent(playerId, 0f, 0f, 0f); finalPickupEntityCount = host.PresentedEntities.CurrentGeneration.Entities.Count(entity => string.Equals(entity.EntityKind, "pickup", StringComparison.Ordinal)); if (expectCargoRejection) { Assert.That(pickupRejection, Is.Not.Null, - "Full cargo did not reject destruction salvage from its Ymir contact fact."); + "Full cargo did not reject destruction salvage at daemon pickup proximity."); Assert.That(pickupRejectionEventCount, Is.EqualTo(1), - "The first destruction-loot Ymir contact transaction must emit one rejection event."); + "The first destruction-loot proximity attempt must emit one rejection event."); Assert.That(pickupRejectionEventIds.Count, Is.EqualTo(pickupRejectionEventCount), - "One Ymir fact identity emitted duplicate player rejection feedback."); + "One daemon proximity attempt emitted duplicate player rejection feedback."); Assert.That(pickupCollectionEventCount, Is.Zero, "Capacity-rejected salvage was also collected."); Assert.That(pickupRejection.ItemKey, Is.Not.Empty); - Assert.That(pickupRejection.EventId, Does.StartWith("ymir-fact:"), - "Rejection feedback must retain the consumed Ymir fact identity."); + Assert.That(pickupRejection.EventId, Does.StartWith("frame:"), + "Capacity feedback must retain its daemon frame/zone/pickup attempt identity."); Assert.That(pickupRejection.Reason, Is.EqualTo("cargo-capacity")); Assert.That(pickupRejection.ScalarValue, Is.EqualTo(1)); Assert.That(pickupRejection.CargoQuantityAfter, @@ -616,15 +627,15 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests else { Assert.That(pickupCollection, Is.Not.Null, - "Destruction-created salvage never reached cargo through a Ymir contact fact."); + "Destruction-created salvage never reached cargo through daemon pickup proximity."); Assert.That(pickupCollectionEventCount, Is.EqualTo(1), - "One destruction-loot Ymir contact transaction must emit one collection event."); + "One durable destruction pickup must emit one collection event."); Assert.That(pickupRejectionEventCount, Is.Zero, "Collectible destruction salvage was also rejected."); Assert.That(pickupCollection.ItemKey, Is.Not.Empty, "Destruction loot must retain its canonical typed item identity."); - Assert.That(pickupCollection.EventId, Does.StartWith("ymir-fact:"), - "Collection feedback must retain the consumed Ymir fact identity."); + Assert.That(pickupCollection.EventId, Does.StartWith("zone:"), + "Collection feedback must retain its stable daemon zone/pickup identity."); Assert.That(pickupCollection.ScalarValue, Is.EqualTo(1)); Assert.That(pickupCollection.CargoQuantityBefore, Is.Zero); Assert.That(pickupCollection.CargoQuantityAfter, Is.EqualTo(1)); @@ -1127,8 +1138,8 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests pickupCollectionEventCount == 0 && string.Equals(pickupRejection.Reason, "cargo-capacity", StringComparison.Ordinal) && pickupRejection.CargoQuantityAfter == pickupRejection.CargoQuantityBefore, - destructionLoot = (pickupCollection?.EventId ?? pickupRejection?.EventId ?? "") - .StartsWith("ymir-fact:", StringComparison.Ordinal), + destructionLoot = pickupEvent != null && !string.IsNullOrWhiteSpace(pickupEvent.EventId) && + !string.IsNullOrWhiteSpace(pickupEvent.ItemKey) && pickupEvent.ScalarValue > 0, initialPickupEntityCount = initialPickupEntityCount, finalPickupEntityCount = finalPickupEntityCount, pickupCollectionEventCount = pickupCollectionEventCount, diff --git a/docs/aetheria-released-package-witness.md b/docs/aetheria-released-package-witness.md index daaec94..2c253d4 100644 --- a/docs/aetheria-released-package-witness.md +++ b/docs/aetheria-released-package-witness.md @@ -1,8 +1,49 @@ # Aetheria released-package witness -## Current result +## Current integration gate -The released-package cold world witness passes with: +The generic `ReleaseConsumerProject` is pinned to released Git packages: + +- `org.gamecult.eve.unity-scene` `0.3.65` 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.16`, commit + `69caf2d027c49227e62ffa24452fc7d717f86ca5`. + +The hand-run integration gate is the warm released-package witness: + +```powershell +pwsh -File .\scripts\run-aetheria-daemon-world-witness.ps1 ` + -CacheState warm ` + -AssetCacheDirectory artifacts\aetheria-daemon-temporal-projection-0363-warm-hd\asset-cache ` + -OutputDirectory artifacts\aetheria-manual-integration ` + -PrimeWarmCacheFromProviderBundle +``` + +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. + +Cold transfer is not a passing integration gate. The current bundle is +`56,193,661` bytes and recent cold runs still exceed the 300-second witness +deadline while asset bytes travel through batched snapshot records. Do not +increase the timeout or describe this as proven; the intended repair is mapped +or network-body transport. `-CacheState cold` remains a diagnostic run. + +## Historical witness ledger + +An earlier released-package cold world witness passed with: - `org.gamecult.eve.unity-scene` `0.3.63`, commit `3135d24bb84f40719b121b51634811c85af0a7af`; @@ -275,9 +316,15 @@ Primary artifacts: - `aetheria-daemon-world.png`: pilot-camera capture; - `aetheria-daemon-map.png`: map-only capture. -## Cold delivery proof +## Cold delivery status -Cold CDN transfer passes with the current released package set. The +Cold CDN transfer is currently failing and is not part of the passing readiness +gate. The latest retained failing runs time out after 300 seconds before the +provider bundle is promoted. The current transport still carries large asset +payloads through batched snapshot records instead of the intended mapped or +network body path. + +The following is historical evidence, not a current claim. A prior `cold-start-lowering` run in `artifacts/aetheria-daemon-current-cold` began with zero cached bodies, received one typed manifest snapshot, transferred bounded payload chunks through the managed CultMesh content session, and atomically @@ -288,9 +335,9 @@ lowered the shield-free provider hull, and preserved pilot/map camera isolation within the unchanged 300-second deadline; total witness time was `102,912.852` milliseconds. -The separate passing `full-session-gameplay` witness starts from the verified -warm body and proves movement, combat, destruction-created canonical loot, Ymir -contact collection, and exactly-once cargo mutation. This proves cold acquisition +The separate historical `full-session-gameplay` witness started from the verified +warm body and proved movement, combat, destruction-created canonical loot, and +exactly-once cargo mutation. This proved cold acquisition and local mapped reuse, not provider-to-client network zero-copy, shared-memory delivery, or GPU zero-copy. Managed network bytes are still copied and fragmented before the transfer owner commits the mapped file. diff --git a/scripts/run-aetheria-daemon-world-witness.ps1 b/scripts/run-aetheria-daemon-world-witness.ps1 index 312915f..ef624f9 100644 --- a/scripts/run-aetheria-daemon-world-witness.ps1 +++ b/scripts/run-aetheria-daemon-world-witness.ps1 @@ -7,10 +7,12 @@ param( [string] $ClientProject = "ReleaseConsumerProject", [int] $Port = 3076, [string] $OutputDirectory = "artifacts\aetheria-daemon", + [string] $AssetCacheDirectory = "", [ValidateSet("auto", "cold", "warm")] - [string] $CacheState = "auto", + [string] $CacheState = "warm", [ValidateSet("released-client-proof", "cargo-capacity-rejection-proof")] [string] $GameplayScenario = "released-client-proof", + [switch] $PrimeWarmCacheFromProviderBundle, [switch] $SkipAssetBundleBuild ) @@ -18,10 +20,17 @@ $ErrorActionPreference = "Stop" $expectedEveUnityCommit = "e08fa08335f99e9edddeb706912eecfad07cb281" $expectedEveFieldsCommit = "c5a4a75c1b727499b16c2dae1895f29e2a9f72f0" $expectedEveUnityUiToolkitCommit = "4d0cbe0185bdc4fc65eb63503a7c5cb578539669" -$expectedCultLibCommit = "419053ebe2325848051c4f4d8ba352cd4286c424" +$expectedCultLibCommit = "69caf2d027c49227e62ffa24452fc7d717f86ca5" $repoRoot = Split-Path -Parent $PSScriptRoot $projectRoot = $ClientProject $outputRoot = if ([IO.Path]::IsPathRooted($OutputDirectory)) { $OutputDirectory } else { Join-Path $repoRoot $OutputDirectory } +$assetCachePath = if ([string]::IsNullOrWhiteSpace($AssetCacheDirectory)) { + Join-Path $outputRoot "asset-cache" +} elseif ([IO.Path]::IsPathRooted($AssetCacheDirectory)) { + $AssetCacheDirectory +} else { + Join-Path $repoRoot $AssetCacheDirectory +} $resultsPath = Join-Path $outputRoot "results.xml" $unityLogPath = Join-Path $outputRoot "unity.log" $bundleBuildLogPath = Join-Path $outputRoot "asset-bundle-build.log" @@ -32,7 +41,6 @@ $factsPath = Join-Path $outputRoot "witness-facts.json" $providerReadyPath = Join-Path $outputRoot "provider-ready.txt" $witnessPath = Join-Path $outputRoot "runtime-witness.json" $replicaPath = Join-Path $outputRoot "eve-unity-replica.cc" -$assetCachePath = Join-Path $outputRoot "asset-cache" $statePath = Join-Path $outputRoot "aetheria-witness-state.cc" $providerBundlePath = Join-Path $AetheriaRoot "Build\EveAssets\StandaloneWindows64\aetheria-world" $daemonProject = Join-Path $AetheriaRoot "Aetheria.State.Daemon\Aetheria.State.Daemon.csproj" @@ -63,10 +71,7 @@ if ($CacheState -eq "cold" -and ($initialBodyCount -ne 0 -or $initialPartialCoun throw "Cold witness cache was not empty before Unity launch. bodies=$initialBodyCount partials=$initialPartialCount" } $cacheWasWarm = @(Get-ChildItem -LiteralPath $assetCachePath -Filter *.body -File -ErrorAction SilentlyContinue).Count -gt 0 -if ($CacheState -eq "warm" -and -not $cacheWasWarm) { - throw "Warm witness requested without an existing verified bundle cache at $assetCachePath" -} -$observedCacheState = if ($cacheWasWarm) { "warm" } else { "cold" } +$observedCacheState = if ($CacheState -eq "warm") { "warm" } elseif ($cacheWasWarm) { "warm" } else { "cold" } $witnessProfile = if ($observedCacheState -eq "cold") { "cold-start-lowering" } else { @@ -119,15 +124,36 @@ if (-not $SkipAssetBundleBuild) { Write-Host "AssetBundle builder PID: $($bundleBuilder.Id)" Write-Host "AssetBundle build log: $bundleBuildLogPath" Write-Host "Poll: Get-Content '$bundleBuildLogPath' -Tail 20" - if (-not $bundleBuilder.WaitForExit(240000)) { + if (-not $bundleBuilder.WaitForExit(360000)) { Stop-Process -Id $bundleBuilder.Id -Force -ErrorAction SilentlyContinue - throw "Aetheria AssetBundle build exceeded 240 seconds. See $bundleBuildLogPath" + throw "Aetheria AssetBundle build exceeded 360 seconds. See $bundleBuildLogPath" } if ($bundleBuilder.ExitCode -ne 0) { Get-Content $bundleBuildLogPath -Tail 160 throw "Aetheria AssetBundle build failed with exit code $($bundleBuilder.ExitCode)" } } +if ($PrimeWarmCacheFromProviderBundle) { + if ($CacheState -ne "warm") { + throw "Local provider-bundle priming is only valid for an explicitly warm witness." + } + if (-not (Test-Path -LiteralPath $providerBundlePath)) { + throw "Provider-owned Aetheria bundle is missing: $providerBundlePath" + } + New-Item -ItemType Directory -Force -Path $assetCachePath | Out-Null + $primeHash = (Get-FileHash -LiteralPath $providerBundlePath -Algorithm SHA256).Hash.ToLowerInvariant() + $primeTarget = Join-Path $assetCachePath "$primeHash.body" + if (-not (Test-Path -LiteralPath $primeTarget)) { + $primePartial = Join-Path $assetCachePath "$primeHash.partial.local-prime" + Copy-Item -LiteralPath $providerBundlePath -Destination $primePartial -Force + if ((Get-FileHash -LiteralPath $primePartial -Algorithm SHA256).Hash.ToLowerInvariant() -ne $primeHash) { + Remove-Item -LiteralPath $primePartial -Force -ErrorAction SilentlyContinue + throw "Locally primed provider bundle failed its content-hash check." + } + Move-Item -LiteralPath $primePartial -Destination $primeTarget -Force + } + Write-Host "Warm cache primed locally from the provider bundle; this is not CDN-transfer evidence: $primeTarget" +} if ($CacheState -eq "warm") { if (-not (Test-Path -LiteralPath $providerBundlePath)) { throw "Provider-owned Aetheria bundle is missing: $providerBundlePath" @@ -278,17 +304,17 @@ try { if ($witnessProfile -eq "full-session-gameplay") { if (-not $facts.combatPresentation -or [string]::IsNullOrWhiteSpace($facts.shotId) -or [double]$facts.lockProgress -le 0.99 -or -not $facts.destructionLoot) { - throw "Full-session witness did not prove combat and Ymir-contact destruction loot." + throw "Full-session witness did not prove combat and daemon-proximity destruction loot." } if ($GameplayScenario -eq "cargo-capacity-rejection-proof") { if ($facts.gameplayScenario -ne $GameplayScenario -or -not $facts.pickupRejection -or [int]$facts.pickupRejectionEventCount -lt 1 -or [int]$facts.pickupCollectionEventCount -ne 0 -or $facts.pickupRejectionReason -ne "cargo-capacity" -or [double]$facts.cargoQuantityBeforeRejection -ne [double]$facts.cargoQuantityAfterRejection) { - throw "Full-session rejection witness did not prove player cargo-capacity refusal from distinct Ymir contact facts." + throw "Full-session rejection witness did not prove player cargo-capacity refusal at daemon pickup proximity." } } elseif (-not $facts.pickupCollection -or [int]$facts.pickupCollectionEventCount -ne 1) { - throw "Full-session collection witness did not prove exactly-once player destruction-loot collection from a Ymir contact fact." + throw "Full-session collection witness did not prove exactly-once player destruction-loot collection at daemon pickup proximity." } } $releaseLock = Get-Content -LiteralPath (Join-Path $repoRoot "ReleaseConsumerProject\Packages\packages-lock.json") -Raw | ConvertFrom-Json