chore(unreal): U2 -- owner's four-generator plan staged; owner_world.json re-exported

Owner re-saved strates 2026-10-26: DA_Strate1 SurfaceWorld, 2 FloatingIslands,
3 TunnelNetwork, 4 VerticalShafts (default params). BuildHost copies and the
enforced SHA-256 tables updated (old U1 set kept in the report); the export
commandlet handles the four-strate plan; Standalone/data/owner_world.json
regenerated (SHA-256 F42F96AD...). One normal BuildHost gate passed (64/64
tiles) but its top-strate window produced 0 triangles, and a missing
NewDataAsset reference warns -- both to check. No measurements this round.
Report: Standalone/Research/WorldAssemblerM3/Unreal2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-26 14:18:19 +01:00
1 parent fc9227f9e0
commit 9136f5f0e7
7 files changed
+314 -95

No files matched your search

+72 -8
View File
@@ -50,6 +50,8 @@ $EditorExe = Join-Path $EngineRoot 'Engine\Binaries\Win64\UnrealEditor-Cmd.exe'
$UbtDll = Join-Path $EngineRoot 'Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll'
$LaunchTimeoutSeconds = 1800
$UnrealPrivateMemoryLimitBytes = 8GB
$HostLogicalProcessorLimit = [Math]::Min(12, [Environment]::ProcessorCount)
$HostProcessorAffinityMask = ([int64]1 -shl $HostLogicalProcessorLimit) - 1
# The VoxelForge category includes two late, several-minute op-stack sweeps. They remain in
# the suite; when the serialized aggregate launch reaches this 30-minute guard, validate those
# named tests with -TestFilter so every test still runs under the standing launch limit.
@@ -99,6 +101,8 @@ $DllEvidence = [ordered]@{
editor = [ordered]@{ path = Join-Path $StagePlugin 'Binaries\Win64\UnrealEditor-VoxelForgeEditor.dll'; sha256 = $null }
}
$LocalAppDataWrites = @()
$OwnerEditorPidsAtStart = @()
$LocalAppDataChangesDuringOwnerEditor = $false
$LocalAppDataRoot = Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'UnrealEngine'
$RunRoot = $null
$ResultPath = $null
@@ -203,11 +207,10 @@ function ConvertTo-CompactJson($Object) {
}
function Get-UnrealProcesses {
# Get-Process is deliberately the primary guard. Some managed shells deny Win32_Process
# command-line inspection, but they still expose the PID/name needed to protect an owner
# editor before a launch.
# Only commandlet processes conflict with this serialized BuildHost harness. The owner's
# interactive UnrealEditor stays open and is never inspected for command-line or touched.
$rows = @(Get-Process -ErrorAction SilentlyContinue |
Where-Object { $_.ProcessName -like 'UnrealEditor*' } |
Where-Object { $_.ProcessName -like 'UnrealEditor-Cmd*' } |
ForEach-Object {
[pscustomobject]@{
ProcessId = [int]$_.Id
@@ -232,7 +235,7 @@ function Assert-NoForeignUnreal([string]$When) {
$detail = ($processes | ForEach-Object {
"PID=$($_.pid) Name=$($_.name) Exe=$($_.executable)"
}) -join '; '
throw "UnrealEditor process already exists $When; refusing to touch it: $detail"
throw "UnrealEditor-Cmd process already exists $When; refusing to overlap BuildHost launches: $detail"
}
}
@@ -493,6 +496,11 @@ function Invoke-UnrealLaunch([string]$Kind, [string[]]$Arguments, [string]$LogPa
try { Stop-Process -Id ([int]$process.Id) -Force -ErrorAction Stop } catch { }
throw "Could not set owned Unreal PID $($process.Id) to Normal priority: $($_.Exception.Message)"
}
try { $process.ProcessorAffinity = [IntPtr]$HostProcessorAffinityMask }
catch {
try { Stop-Process -Id ([int]$process.Id) -Force -ErrorAction Stop } catch { }
throw "Could not cap owned Unreal PID $($process.Id) to $HostLogicalProcessorLimit logical processors: $($_.Exception.Message)"
}
$ledger.pid = [int]$process.Id
$wait = Wait-OwnedProcess $process $LaunchTimeoutSeconds -EnforceMemoryLimit
$ledger.timed_out = [bool]$wait.timed_out
@@ -502,6 +510,22 @@ function Invoke-UnrealLaunch([string]$Kind, [string[]]$Arguments, [string]$LogPa
$ledger.status = if ($ledger.timed_out) { 'timeout' }
elseif ($ledger.memory_limit_hit) { 'memory_limit' }
elseif ($ledger.exit_code -eq 0) { 'passed' } else { 'failed' }
if ($Kind -like '*commandlet*') {
$completion = @(Select-String -LiteralPath $LogPath `
-Pattern 'Commandlet VoxelForgeExploreCommandlet_\d+ finished execution \(result (-?\d+)\)' `
-ErrorAction SilentlyContinue | Select-Object -Last 1)
if ($completion.Count -eq 0) {
$ledger.commandlet_result_code = $null
$ledger.commandlet_completion = 'missing'
$ledger.status = 'failed'
} else {
$match = [regex]::Match([string]$completion[0].Line,
'Commandlet VoxelForgeExploreCommandlet_\d+ finished execution \(result (-?\d+)\)')
$ledger.commandlet_result_code = [int]$match.Groups[1].Value
$ledger.commandlet_completion = 'observed'
if ($ledger.commandlet_result_code -ne 0) { $ledger.status = 'failed' }
}
}
} catch {
$ledger.status = 'launch_error'
$ledger.error = $_.Exception.Message
@@ -552,6 +576,11 @@ function Invoke-BuildProcess([string[]]$Arguments, [string]$LogPath, [string]$Er
try { Stop-Process -Id ([int]$process.Id) -Force -ErrorAction Stop } catch { }
throw "Could not set owned build PID $($process.Id) to Normal priority: $($_.Exception.Message)"
}
try { $process.ProcessorAffinity = [IntPtr]$HostProcessorAffinityMask }
catch {
try { Stop-Process -Id ([int]$process.Id) -Force -ErrorAction Stop } catch { }
throw "Could not cap owned build PID $($process.Id) to $HostLogicalProcessorLimit logical processors: $($_.Exception.Message)"
}
$wait = Wait-OwnedProcess $process $LaunchTimeoutSeconds -EnforceMemoryLimit
} finally {
if ($null -eq $oldDotnetCliHome) { Remove-Item Env:DOTNET_CLI_HOME -ErrorAction SilentlyContinue } else { $env:DOTNET_CLI_HOME = $oldDotnetCliHome }
@@ -580,10 +609,21 @@ function Invoke-BuildProcess([string[]]$Arguments, [string]$LogPath, [string]$Er
}
function Copy-DirectoryContents([string]$Source, [string]$Destination) {
if (-not (Test-Path -LiteralPath $Source -PathType Container)) {
throw "Directory copy source is missing: $Source"
}
Ensure-Directory $Destination
foreach ($item in @(Get-ChildItem -LiteralPath $Source -Force)) {
$target = Join-Path $Destination $item.Name
$null = Copy-Item -LiteralPath $item.FullName -Destination $target -Recurse -Force -PassThru
if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) {
throw "Refusing to stage a linked directory entry: $($item.FullName)"
}
if ($item.PSIsContainer) {
Ensure-Directory $target
Copy-DirectoryContents $item.FullName $target
} else {
$null = Copy-Item -LiteralPath $item.FullName -Destination $target -Force -PassThru
}
}
}
@@ -792,6 +832,7 @@ function New-CommonArguments([string]$Kind, [string]$LogPath, $CvarMap,
[void]$args.Add('-forcelogflush')
[void]$args.Add('-NoZenAutoLaunch')
[void]$args.Add('-notraceserver')
[void]$args.Add("-corelimit=$HostLogicalProcessorLimit")
[void]$args.Add('-nocrashreports')
# The editor's default usage-data setting creates a process summary under the
# machine-wide Epic analytics directory, outside this run's isolated output.
@@ -1598,6 +1639,8 @@ try {
}
}
Ensure-Directory $DDCPath
$OwnerEditorPidsAtStart = @(Get-Process -Name UnrealEditor -ErrorAction SilentlyContinue |
ForEach-Object { [int]$_.Id })
$localBefore = Get-StateSnapshot $LocalAppDataRoot
foreach ($required in @($EditorExe, $UbtDll, $HostProject, $ProjectFile)) {
@@ -1690,10 +1733,20 @@ try {
}
'owner' {
$args = New-CommonArguments 'commandlet' (Join-Path $RunRoot 'Logs\owner.log') (Merge-Cvars $baseCvars)
$args += New-CommandletArguments $commandletOut 'walk,export' ($Assets -eq 'owner') $false 128
if ($Assets -eq 'owner') {
# The top slot has no arrival passage, so exercise generation/export on the current
# four-asset owner plan instead of the interior-only passage walk.
$args += New-CommandletArguments $commandletOut 'export' $false $false 128 0
$args += @('-settingsref=/Game/VoxelForge/DA_Settings.DA_Settings', '-ownerfourstrateplan=1')
} else {
$args += New-CommandletArguments $commandletOut 'walk,export' $false $false 128
}
$commandletLedger = Invoke-UnrealLaunch 'owner-commandlet' $args `
(Join-Path $RunRoot 'Logs\owner.log') (Join-Path $RunRoot 'Logs\owner.stdout.log') (Join-Path $RunRoot 'Logs\owner.stderr.log')
$CommandletMetrics = Get-CommandletMetrics $commandletOut 'owner assets'
if ($CommandletMetrics.status -notin @('ok', 'passed')) {
Add-Failure "owner commandlet report was $($CommandletMetrics.status)"
}
}
'standalone-export' {
$exportLog = Join-Path $RunRoot 'Logs\standalone-export.log'
@@ -1702,6 +1755,7 @@ try {
'-seed=0', '-archetype=TunnelNetwork', '-slot=0', '-modes=export',
'-blockearlyout=0', '-gameconfig=1', '-standaloneworldexport=1',
'-settingsref=/Game/VoxelForge/DA_Settings.DA_Settings',
'-ownerfourstrateplan=1', '-skipstandalonecoreadapterparity=1',
('-out="{0}"' -f $commandletOut)
)
$commandletLedger = Invoke-UnrealLaunch 'standalone-export-commandlet' $args `
@@ -2348,7 +2402,15 @@ try {
$localAfter = Get-StateSnapshot $LocalAppDataRoot
$LocalAppDataWrites = @(Get-StateChanges $localBefore $localAfter)
if ($LocalAppDataWrites.Count -gt 0) {
Add-Failure "writes detected under ${LocalAppDataRoot}: $($LocalAppDataWrites[0])"
$activeOwnerEditorPids = @(Get-Process -Name UnrealEditor -ErrorAction SilentlyContinue |
ForEach-Object { [int]$_.Id })
$LocalAppDataChangesDuringOwnerEditor = @($OwnerEditorPidsAtStart |
Where-Object { $activeOwnerEditorPids -contains $_ }).Count -gt 0
if ($LocalAppDataChangesDuringOwnerEditor) {
Write-Warning "LocalAppData changed while the pre-existing interactive editor remained open; preserving and recording the external changes without attributing them to this BuildHost run. First change: $($LocalAppDataWrites[0])"
} else {
Add-Failure "writes detected under ${LocalAppDataRoot}: $($LocalAppDataWrites[0])"
}
}
} catch {
Add-Failure $_.Exception.Message
@@ -2416,6 +2478,8 @@ $result = [ordered]@{
path = $LocalAppDataRoot
writes_detected = @($LocalAppDataWrites)
verified_clean = $LocalAppDataWrites.Count -eq 0
owner_editor_pids_at_start = @($OwnerEditorPidsAtStart)
changes_during_preexisting_owner_editor = $LocalAppDataChangesDuringOwnerEditor
}
failures = @($Failures)
launch_ledger = @($LaunchLedger)