Files
ARCADIA-Studio/harness/experiments/allocation-drawing-301126f-20260923/native/launch.ps1
T

70 lines
7.6 KiB
PowerShell

#Requires -Version 7.0
param([ValidateRange(1,99)][int]$Run=1)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'verify.ps1')
if (Test-Path -LiteralPath (Join-Path $PSScriptRoot 'UNACCEPTED.json')) {throw 'Trial quarantined; do not launch or relearn bindings'}
$manifestPath=Join-Path $PSScriptRoot 'input-manifest.json'
$stagePath=Join-Path $PSScriptRoot 'staging-record.json'
$manifest=Read-NativeJson $manifestPath
$stage=Read-NativeJson $stagePath
if ($manifest.source_commit -notmatch '^[0-9a-f]{40}$' -or $stage.source_commit -cne $manifest.source_commit) { throw 'Wrong staged candidate' }
Assert-NativeFiles @($stage.manifest)
Assert-NativeFiles $manifest.immutable_files
$stageHash=Get-NativeHash $stagePath
$bindingPaths=@((Join-Path $PSScriptRoot 'OA-binding.json'),(Join-Path $PSScriptRoot 'SA-binding.json'))
$bindingBefore=@($bindingPaths | Where-Object {Test-Path -LiteralPath $_} | ForEach-Object {Get-NativeRecord $_})
$working=if ($Run -eq 1) {$manifest.initial_working.path} else {$manifest.saved_working_path}
$runRoot=Join-Path $PSScriptRoot ('runs/run-{0:d2}' -f $Run)
$profile=Join-Path $PSScriptRoot 'profile'
$cwd=Join-Path $PSScriptRoot 'unrelated-cwd'
$previousHash=$null
if (Test-Path -LiteralPath $runRoot) { throw 'Never overwrite existing run evidence' }
if ($Run -eq 1) {
if (Test-Path -LiteralPath (Join-Path $PSScriptRoot 'runs')) { throw 'First run requires fresh runs directory' }
Assert-NativeFiles @($manifest.initial_working)
if (-not $manifest.saved_working_prelaunch_absent -or (Test-Path -LiteralPath $manifest.saved_working_path)) {throw 'Save As source destination must be absent before first launch'}
foreach($dir in @($profile,$cwd)) { if (-not (Test-Path -LiteralPath $dir -PathType Container) -or @(Get-ChildItem -LiteralPath $dir -Force).Count) { throw 'First run requires staged empty private directories' } }
} else {
$prior=Join-Path $PSScriptRoot ('runs/run-{0:d2}' -f ($Run-1))
$priorClose=Read-NativeJson (Join-Path $prior 'normal-close.json')
$priorLaunch=Read-NativeJson (Join-Path $prior 'launch.json')
if ($priorClose.exit_code -ne 0 -or $priorClose.stdout.bytes -ne 0 -or $priorClose.stderr.bytes -ne 0 -or -not $priorClose.immutable_inputs_unchanged -or $priorClose.source_commit -cne $manifest.source_commit -or $priorClose.launch_sha256 -cne (Get-NativeHash (Join-Path $prior 'launch.json')) -or $priorLaunch.manifest_sha256 -cne (Get-NativeHash $manifestPath) -or $priorLaunch.pid -ne $priorClose.pid -or $priorLaunch.started_utc -cne $priorClose.started_utc) { throw 'Previous successful bound close required' }
Assert-NativeFiles @($priorClose.working_file,$priorClose.stdout,$priorClose.stderr)
Assert-NativeFiles $priorClose.relation_bindings_after
if ((@($priorClose.relation_bindings_after)|ConvertTo-Json -Depth 20 -Compress) -cne (@($bindingBefore)|ConvertTo-Json -Depth 20 -Compress)) {throw 'Exact prior relation binding inventory required; never delete or relearn bindings'}
Assert-NativeFiles $priorClose.working_bundle
if (($priorClose.working_bundle|ConvertTo-Json -Depth 20 -Compress) -cne (@(Get-NativeSourceBundle)|ConvertTo-Json -Depth 20 -Compress)) {throw 'Complete source bundle changed since prior close'}
$previousHash=Get-NativeHash (Join-Path $prior 'normal-close.json')
}
$package=Join-Path $PSScriptRoot 'package'
$childPath=$package+';'+(Join-Path $env:SystemRoot 'System32')+';'+$env:SystemRoot+';'+(Join-Path $env:SystemRoot 'System32/Wbem')
$state=[ordered]@{run=$Run;source_commit=$manifest.source_commit;evaluator_source_commit=$manifest.evaluator_source_commit;manifest_sha256=(Get-NativeHash $manifestPath);staging_sha256=$stageHash;script_sha256=(Get-NativeHash $PSCommandPath);native_plan_sha256=$manifest.native_plan_sha256;previous_close_sha256=$previousHash;binary=$manifest.binary;runtime=$manifest.runtime;working_file_before=(Get-NativeRecord $working);working_bundle_before=@(Get-NativeSourceBundle);saved_working_path=$manifest.saved_working_path;relation_bindings_before=$bindingBefore;localappdata=$profile;working_directory=$cwd;child_path=$childPath;arguments=@();pid=$null;started_utc=$null;environment_scope='Child ProcessStartInfo only'}
$process=$null;$outStream=$null;$errStream=$null
try {
[void][IO.Directory]::CreateDirectory((Join-Path $runRoot 'logs'))
$stdout=Join-Path $runRoot 'logs/studio.stdout.log';$stderr=Join-Path $runRoot 'logs/studio.stderr.log'
$outStream=[IO.File]::Open($stdout,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::Read)
$errStream=[IO.File]::Open($stderr,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::Read)
$info=[Diagnostics.ProcessStartInfo]::new();$info.FileName=$manifest.binary.path;$info.WorkingDirectory=$cwd;$info.UseShellExecute=$false;$info.CreateNoWindow=$true;$info.WindowStyle=[Diagnostics.ProcessWindowStyle]::Hidden;$info.RedirectStandardOutput=$true;$info.RedirectStandardError=$true
$info.Environment['LOCALAPPDATA']=$profile;$info.Environment['PATH']=$childPath
$process=[Diagnostics.Process]::new();$process.StartInfo=$info
if (-not $process.Start()) { throw 'Process start failed' }
$outCopy=$process.StandardOutput.BaseStream.CopyToAsync($outStream);$errCopy=$process.StandardError.BaseStream.CopyToAsync($errStream)
$state.pid=$process.Id;$state.started_utc=$process.StartTime.ToUniversalTime().ToString('o')
Write-NativeJson (Join-Path $runRoot 'launch.json') $state
$state|ConvertTo-Json -Depth 15|Write-Output
$process.WaitForExit();[Threading.Tasks.Task]::WaitAll([Threading.Tasks.Task[]]@($outCopy,$errCopy))
$outStream.Flush($true);$errStream.Flush($true);$outStream.Dispose();$outStream=$null;$errStream.Dispose();$errStream=$null
$unchanged=$true;$verificationError=$null
try { if($null -ne $bindingBefore){Assert-NativeFiles @($bindingBefore)}; Assert-NativeFiles $manifest.immutable_files; Assert-NativeFiles @($stage.manifest);if ((Get-NativeHash $stagePath) -cne $stageHash) {throw 'Staging receipt changed'} } catch {$unchanged=$false;$verificationError=$_.Exception.Message}
$working=$manifest.saved_working_path
Write-NativeJson (Join-Path $runRoot 'normal-close.json') ([ordered]@{run=$Run;source_commit=$manifest.source_commit;evaluator_source_commit=$manifest.evaluator_source_commit;launch_sha256=(Get-NativeHash (Join-Path $runRoot 'launch.json'));pid=$state.pid;started_utc=$state.started_utc;observed_exit_utc=[DateTime]::UtcNow.ToString('o');exit_code=$process.ExitCode;immutable_inputs_unchanged=$unchanged;verification_error=$verificationError;baseline_file=(Get-NativeRecord (Join-Path $PSScriptRoot 'baseline/B.input.json'));working_file=(Get-NativeRecord $working);working_bundle=@(Get-NativeSourceBundle);relation_bindings_after=@($bindingPaths | Where-Object {Test-Path -LiteralPath $_} | ForEach-Object {Get-NativeRecord $_});stdout=(Get-NativeRecord $stdout);stderr=(Get-NativeRecord $stderr);scope='Observed process exit only; close cause and native interaction acceptance require root observation. No forced termination by observer.'})
if (-not $unchanged) {throw $verificationError}
if ($process.ExitCode -ne 0 -or (Get-Item -LiteralPath $stdout).Length -ne 0 -or (Get-Item -LiteralPath $stderr).Length -ne 0) {throw 'Nonzero process exit or nonempty GUI logs; preserve evidence and investigate'}
} catch {
Write-NativeJson (Join-Path $runRoot 'observer-failure.json') ([ordered]@{utc=[DateTime]::UtcNow.ToString('o');pid=$state.pid;error=$_.Exception.Message;owned_process_not_killed=$true})
throw
} finally {
if ($null -ne $outStream) {$outStream.Dispose()};if ($null -ne $errStream) {$errStream.Dispose()};if ($null -ne $process) {$process.Dispose()}
}