--- original/oracle/build.ps1 +++ runtime-bound/oracle/build.ps1 @@ -7,7 +7,9 @@ [Parameter(Mandatory=$true,ParameterSetName='Build')][string]$SerdeJsonRlib, [Parameter(Mandatory=$true,ParameterSetName='Build')][string]$WindowsImportLibDirectory, [Parameter(Mandatory=$true,ParameterSetName='Build')][string]$Output, - [Parameter(ParameterSetName='Build')][string]$NativeVerifier=(Join-Path $PSScriptRoot '../../scenario-spacing-native-07e611f-20260913/verify.ps1'), + [Parameter(Mandatory=$true,ParameterSetName='Build')][string]$NativeVerifier, + [Parameter(Mandatory=$true,ParameterSetName='Build')][ValidatePattern('^[0-9a-f]{64}$')][string]$NativeVerifierSha256, + [Parameter(Mandatory=$true,ParameterSetName='Build')][string]$RuntimeDll, [Parameter(Mandatory=$true,ParameterSetName='Validate')][switch]$ValidateOnly ) Set-StrictMode -Version Latest @@ -21,6 +23,16 @@ function Assert-Dependency($Fingerprint,[string]$Name,[UInt64]$Value) { $edges=@($Fingerprint.deps|Where-Object {$_[1] -ceq $Name}) if($edges.Count -ne 1 -or [UInt64]$edges[0][3] -ne $Value){throw "Cargo dependency fingerprint differs or is ambiguous: $Name"} +} +function Select-OracleSources($Inputs,$SourcePaths) { + if(@($SourcePaths).Count -ne 5 -or @($SourcePaths|Sort-Object -Unique).Count -ne 5){throw 'Exactly five distinct canonical oracle source paths required'} + $selected=@(foreach($path in $SourcePaths){ + if([IO.Path]::GetFullPath($path) -cne $path){throw 'Oracle source path is not canonical'} + $matches=@($Inputs|Where-Object {$_.path -ieq $path}) + if($matches.Count -ne 1){throw ('Oracle source missing or duplicated in build capture: '+$path)} + $matches[0] + }) + return $selected } if($ValidateOnly) { foreach($script in @($PSCommandPath,(Join-Path $PSScriptRoot 'check.ps1'))){ @@ -42,11 +54,30 @@ {Assert-Dependency $sample 'serde_json' ([UInt64]1)}, {Assert-Dependency ('{"deps":[[1,"serde_json",false,1],[2,"serde_json",false,1]]}'|ConvertFrom-Json) 'serde_json' ([UInt64]1)} )) {try {& $probe|Out-Null} catch {$rejected++;continue};throw 'A fingerprint refusal probe unexpectedly passed.'} - [ordered]@{status='passed';parsed_scripts=2;positive_cases=4;negative_cases=$rejected;script_sha256=(Get-FileHash -LiteralPath $PSCommandPath -Algorithm SHA256).Hash.ToLowerInvariant();check_script_sha256=(Get-FileHash -LiteralPath (Join-Path $PSScriptRoot 'check.ps1') -Algorithm SHA256).Hash.ToLowerInvariant();compiler_executed=$false;gate_admitted=$false;scope='Syntax and synthetic fingerprint guards only; no dependency selection, compilation or oracle execution'}|ConvertTo-Json + $sourcePaths=@('C:\probe\oracle\build.ps1','C:\probe\oracle\check.ps1','C:\probe\oracle\main.rs','C:\probe\incident-workspace.arcadia','C:\verifier\verify.ps1') + $sourceRecords=@($sourcePaths|ForEach-Object {[pscustomobject]@{path=$_;bytes=1;sha256=('0'*64)}}) + if(@(Select-OracleSources $sourceRecords $sourcePaths).Count -ne 5){throw 'Canonical source membership oracle failed'} + $sourceRefusals=0 + $uncanonical=@($sourceRecords|ForEach-Object {[pscustomobject]@{path=$_.path;bytes=$_.bytes;sha256=$_.sha256}}) + $uncanonical[3].path='C:\probe\oracle\..\incident-workspace.arcadia' + $membershipCases=@( + @{name='missing_fixture';records=@($sourceRecords|Where-Object {$_.path -ne $sourcePaths[3]})}, + @{name='unnormalized_fixture';records=$uncanonical}, + @{name='duplicate_fixture';records=(@($sourceRecords)+@($sourceRecords[3]))} + ) + foreach($case in $membershipCases){ + $didReject=$false;try{Select-OracleSources $case.records $sourcePaths|Out-Null}catch{$didReject=$true} + if(-not $didReject){throw ('Source membership admitted '+$case.name)};$sourceRefusals++ + } + [ordered]@{status='passed';parsed_scripts=2;positive_cases=5;negative_cases=$rejected;source_membership_negative_cases=$sourceRefusals;script_sha256=(Get-FileHash -LiteralPath $PSCommandPath -Algorithm SHA256).Hash.ToLowerInvariant();check_script_sha256=(Get-FileHash -LiteralPath (Join-Path $PSScriptRoot 'check.ps1') -Algorithm SHA256).Hash.ToLowerInvariant();compiler_executed=$false;gate_admitted=$false;scope='Syntax and synthetic fingerprint guards only; no dependency selection, compilation or oracle execution'}|ConvertTo-Json return } -# Reuse the existing native gate verifier; do not reproduce its five-stage parser. +# Reuse the frozen native verifier; preserve its complete five-stage admission. +$NativeVerifier=(Get-Item -LiteralPath $NativeVerifier).FullName +if((Get-FileHash -LiteralPath $NativeVerifier -Algorithm SHA256).Hash.ToLowerInvariant() -cne $NativeVerifierSha256){throw 'Explicit reviewed native verifier changed'} +$RuntimeDll=(Get-Item -LiteralPath $RuntimeDll).FullName +if((Get-Item -LiteralPath $RuntimeDll).Length -ne 190464 -or (Get-FileHash -LiteralPath $RuntimeDll -Algorithm SHA256).Hash.ToLowerInvariant() -cne 'aa282fa47bb2c4e210cea5e470cdbdca58568f0be0bc73e404d86ab9cd95ef6a'){throw 'Explicit runtime DLL differs from pinned bytes'} . $NativeVerifier $nativePin=$SourcePin $Gate=(Resolve-Path -LiteralPath $Gate).Path @@ -94,21 +125,23 @@ $clang=(Get-Command clang -CommandType Application).Source $gateVersions=Read-NativeJson (Join-Path $Gate 'toolchain-versions.json') if($rustc -ine $gateVersions.rustcPath -or $clang -ine $gateVersions.clangPath){throw 'Resolved compiler/linker paths differ from the successful gate.'} -$source=Join-Path $PSScriptRoot 'main.rs' -$fixture=Join-Path $PSScriptRoot '../incident-workspace.arcadia' +$source=(Get-Item -LiteralPath (Join-Path $PSScriptRoot 'main.rs')).FullName +$fixture=(Get-Item -LiteralPath (Join-Path $PSScriptRoot '../incident-workspace.arcadia')).FullName if((Get-NativeHash $fixture) -cne '74cd2c1dd5eed7eb19895a752bb8ff9ce93655f7638a5312c96f56218568ebb3'){throw 'Embedded original fixture changed.'} +$oracleSourcePaths=@($PSCommandPath,$source,$fixture,(Join-Path $PSScriptRoot 'check.ps1'),$NativeVerifier)|ForEach-Object {[IO.Path]::GetFullPath($_)} function Oracle-BuildInputs { - $paths=@($PSCommandPath,$source,$fixture,(Join-Path $PSScriptRoot 'check.ps1'),$NativeVerifier,$rustc,$clang,$build.externalEnvironmentPath,$desktop.exe,$build.binary.path) + $paths=@($oracleSourcePaths)+@($rustc,$clang,$build.externalEnvironmentPath,$desktop.exe,$build.binary.path,$RuntimeDll) # Complete rustc dependency search inputs, including proc-macro DLLs; exclude test EXEs/logs. $paths+=@(Get-ChildItem -LiteralPath $deps -File|Where-Object {$_.Extension -in @('.rlib','.rmeta','.dll','.lib','.a')}|ForEach-Object {$_.FullName}) $paths+=@(Get-ChildItem -LiteralPath $native -File -Recurse|ForEach-Object {$_.FullName}) foreach($folder in @($coreFingerprint,$serdeFingerprint,$desktop.folder)){$paths+=@(Get-ChildItem -LiteralPath $folder -File|ForEach-Object {$_.FullName})} $paths+=@((Join-Path $deps ('oa_core-'+$coreSuffix+'.d')),(Join-Path $deps ('serde_json-'+$serdeSuffix+'.d')),(Join-Path $deps ('arcadia_studio-'+$desktop.suffix+'.d'))) - $paths+=@(Get-ChildItem -LiteralPath $Gate -File|Where-Object {$_.Extension -in @('.json','.ps1','.log')}|ForEach-Object {$_.FullName}) + $paths+=@(Get-ChildItem -LiteralPath $Gate -File|Where-Object {$_.Extension -in @('.json','.ps1','.log','.md')}|ForEach-Object {$_.FullName}) @($paths|ForEach-Object {[IO.Path]::GetFullPath($_)}|Sort-Object -Unique|ForEach-Object {$item=Get-Item -LiteralPath $_;[ordered]@{path=$item.FullName;bytes=$item.Length;sha256=(Get-NativeHash $item.FullName)}}) } [void][IO.Directory]::CreateDirectory($Output) $before=@(Oracle-BuildInputs) +$sourceRecords=@(Select-OracleSources $before $oracleSourcePaths) Write-NativeJson (Join-Path $Output 'inputs-before.json') $before $PSNativeCommandUseErrorActionPreference=$false $rustcVersion=@(& $rustc --version --verbose 2>&1|ForEach-Object {$_.ToString()});$rustcVersionExit=$LASTEXITCODE @@ -122,18 +155,28 @@ & $rustc @compilerArgs *> (Join-Path $Output 'compile.log') $exit=$LASTEXITCODE $end=[DateTime]::UtcNow.ToString('o') -$after=@();$failure=$null;$unchanged=$false +$after=@();$failure=$null;$unchanged=$false;$runtimeSource=$null;$runtimeCopy=$null try { $after=@(Oracle-BuildInputs) + [void](Select-OracleSources $after $oracleSourcePaths) Write-NativeJson (Join-Path $Output 'inputs-after.json') $after Assert-NativeNames @($before|ForEach-Object {$_.path}) @($after|ForEach-Object {$_.path}) $byPath=@{};foreach($f in $before){$byPath[$f.path]=$f} foreach($f in $after){$old=$byPath[$f.path];if($old.bytes -ne $f.bytes -or $old.sha256 -cne $f.sha256){throw "Build input changed: $($f.path)"}} Assert-CurrentNativeSource $Gate [void](Assert-BuildEvidence $Gate) + if($exit -eq 0){ + $runtimeSource=@($before|Where-Object {$_.path -ieq $RuntimeDll}) + if($runtimeSource.Count -ne 1){throw 'Runtime source missing from build manifest'} + $runtimeSource=$runtimeSource[0] + $runtimePath=Join-Path $Output 'libunwind.dll' + [IO.File]::Copy($RuntimeDll,$runtimePath,$false) + $runtimeCopy=[ordered]@{path=$runtimePath;bytes=(Get-Item -LiteralPath $runtimePath).Length;sha256=(Get-NativeHash $runtimePath)} + if($runtimeCopy.bytes -ne $runtimeSource.bytes -or $runtimeCopy.sha256 -cne $runtimeSource.sha256){throw 'Adjacent runtime copy differs from captured source'} + } $unchanged=$true } catch {$failure=$_.Exception.Message} $binary=if(Test-Path -LiteralPath $exe){$item=Get-Item -LiteralPath $exe;[ordered]@{path=$exe;bytes=$item.Length;sha256=(Get-NativeHash $exe)}}else{$null} -Write-NativeJson (Join-Path $Output 'build-receipt.json') ([ordered]@{source_commit=$SourcePin;gate=$Gate;gate_build_receipt_sha256=(Get-NativeHash (Join-Path $Gate 'build-receipt.json'));command=@($rustc)+$compilerArgs;start_utc=$start;end_utc=$end;exit_code=$exit;inputs_unchanged=$unchanged;postflight_failure=$failure;binary=$binary;fingerprint_chain=[ordered]@{desktop_fingerprint=$desktop.folder;desktop_exe=$desktop.exe;core=$core;core_fingerprint_value=$coreValue;serde_json=$serde;serde_fingerprint_value=$serdeValue};before_manifest_sha256=(Get-NativeHash (Join-Path $Output 'inputs-before.json'));after_manifest_sha256=if(Test-Path -LiteralPath (Join-Path $Output 'inputs-after.json')){Get-NativeHash (Join-Path $Output 'inputs-after.json')}else{$null};toolchain_sha256=(Get-NativeHash (Join-Path $Output 'toolchain.json'));compile_log_sha256=(Get-NativeHash (Join-Path $Output 'compile.log'));oracle_executed=$false;scope='Standalone oracle compilation only; gate hash plus production desktop→core→serde Cargo fingerprint linkage and unchanged link inputs. No native acceptance.'}) -if($exit -ne 0 -or -not $unchanged -or $null -eq $binary -or $binary.bytes -le 0){throw 'Oracle build or provenance failed; preserve all output.'} +Write-NativeJson (Join-Path $Output 'build-receipt.json') ([ordered]@{schema='scenario_spacing_oracle_build_v2';source_commit=$SourcePin;native_verifier=[ordered]@{path=$NativeVerifier;sha256=$NativeVerifierSha256};runtime_source=$runtimeSource;runtime=$runtimeCopy;source_files=$sourceRecords;gate=$Gate;gate_build_receipt_sha256=(Get-NativeHash (Join-Path $Gate 'build-receipt.json'));command=@($rustc)+$compilerArgs;start_utc=$start;end_utc=$end;exit_code=$exit;inputs_unchanged=$unchanged;postflight_failure=$failure;binary=$binary;fingerprint_chain=[ordered]@{desktop_fingerprint=$desktop.folder;desktop_exe=$desktop.exe;core=$core;core_fingerprint_value=$coreValue;serde_json=$serde;serde_fingerprint_value=$serdeValue};before_manifest_sha256=(Get-NativeHash (Join-Path $Output 'inputs-before.json'));after_manifest_sha256=if(Test-Path -LiteralPath (Join-Path $Output 'inputs-after.json')){Get-NativeHash (Join-Path $Output 'inputs-after.json')}else{$null};toolchain_sha256=(Get-NativeHash (Join-Path $Output 'toolchain.json'));compile_log_sha256=(Get-NativeHash (Join-Path $Output 'compile.log'));oracle_executed=$false;scope='Standalone oracle compilation only; gate hash plus production desktop -> core -> serde Cargo fingerprint linkage and unchanged link inputs. No native acceptance.'}) +if($exit -ne 0 -or -not $unchanged -or $null -eq $binary -or $binary.bytes -le 0 -or $null -eq $runtimeCopy -or $null -ne $failure){throw 'Oracle build or provenance failed; preserve all output.'} Write-Output 'Standalone oracle built and hash-bound; oracle and native workflows have not run.' --- original/oracle/check.ps1 +++ runtime-bound/oracle/check.ps1 @@ -1,56 +1,116 @@ #Requires -Version 7.0 -[CmdletBinding()] +[CmdletBinding(DefaultParameterSetName='Run')] param( - [Parameter(Mandatory=$true)][string]$OracleExe, - [Parameter(Mandatory=$true)][ValidatePattern('^[0-9a-f]{64}$')][string]$OracleSha256, - [Parameter(Mandatory=$true)][string]$BuildReceipt, - [Parameter(Mandatory=$true)][ValidatePattern('^[0-9a-f]{64}$')][string]$BuildReceiptSha256, - [Parameter(Mandatory=$true)][ValidateSet('self-check','same','repair-c','lane','message')][string]$Mode, - [Parameter(Mandatory=$true)][string]$Baseline, + [Parameter(Mandatory=$true,ParameterSetName='Run')][ValidatePattern('^[0-9a-f]{40}$')][string]$SourcePin, + [Parameter(Mandatory=$true,ParameterSetName='Run')][string]$OracleExe, + [Parameter(Mandatory=$true,ParameterSetName='Run')][ValidatePattern('^[0-9a-f]{64}$')][string]$OracleSha256, + [Parameter(Mandatory=$true,ParameterSetName='Run')][string]$BuildReceipt, + [Parameter(Mandatory=$true,ParameterSetName='Run')][ValidatePattern('^[0-9a-f]{64}$')][string]$BuildReceiptSha256, + [Parameter(Mandatory=$true,ParameterSetName='Run')][ValidateSet('self-check','same','repair-c','lane','message')][string]$Mode, + [Parameter(Mandatory=$true,ParameterSetName='Run')][string]$Baseline, [string]$Actual, - [Parameter(Mandatory=$true)][string]$Output, + [Parameter(Mandatory=$true,ParameterSetName='Run')][string]$Output, [string]$View, [string]$Target, - [string]$Coordinate + [string]$Coordinate, + [Parameter(Mandatory=$true,ParameterSetName='Validate')][switch]$ValidateOnly ) Set-StrictMode -Version Latest $ErrorActionPreference='Stop' function Hash([string]$Path){(Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()} function Record([string]$Path){$file=Get-Item -LiteralPath $Path;[ordered]@{path=$file.FullName;bytes=$file.Length;sha256=(Hash $file.FullName)}} function Write-NewJson([string]$Path,$Value){$stream=[IO.File]::Open($Path,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::Read);try{$bytes=[Text.UTF8Encoding]::new($false).GetBytes(($Value|ConvertTo-Json -Depth 12)+"`n");$stream.Write($bytes,0,$bytes.Length);$stream.Flush($true)}finally{$stream.Dispose()}} +function Assert-AcceptedBuild($Build,$ExeRecord,$RuntimeRecord,[string]$ExpectedSourcePin) { + if($ExpectedSourcePin -cnotmatch '^[0-9a-f]{40}$'){throw 'Explicit reviewed source pin required'} + if($Build.schema -cne 'scenario_spacing_oracle_build_v2' -or $Build.source_commit -cne $ExpectedSourcePin -or $Build.exit_code -ne 0 -or -not $Build.inputs_unchanged -or $null -ne $Build.postflight_failure){throw 'Successful spacing-oracle build for the explicit reviewed source required'} + if($null -eq $Build.binary -or $Build.binary.path -ine $ExeRecord.path -or $Build.binary.bytes -ne $ExeRecord.bytes -or $Build.binary.sha256 -cne $ExeRecord.sha256 -or $ExeRecord.bytes -le 0){throw 'Receipt does not describe supplied executable'} + $adjacent=Join-Path (Split-Path -Parent $ExeRecord.path) 'libunwind.dll' + if($null -eq $Build.runtime -or $null -eq $Build.runtime_source -or $Build.runtime.path -ine $adjacent -or $RuntimeRecord.path -ine $adjacent){throw 'Receipt must bind the actual adjacent runtime DLL'} + foreach($r in @($Build.runtime_source,$Build.runtime,$RuntimeRecord)){ + if($r.bytes -ne 190464 -or $r.sha256 -cne 'aa282fa47bb2c4e210cea5e470cdbdca58568f0be0bc73e404d86ab9cd95ef6a'){throw 'Runtime DLL differs from exact pinned bytes'} + } +} +function Assert-Records($Files){foreach($f in $Files){$current=Record $f.path;if($current.bytes -ne $f.bytes -or $current.sha256 -cne $f.sha256){throw ('Changed input: '+$f.path)}}} +function Read-Json([string]$Path){Get-Content -Raw -LiteralPath $Path|ConvertFrom-Json} +if($ValidateOnly){ + $tokens=$null;$errors=$null;[void][Management.Automation.Language.Parser]::ParseFile($PSCommandPath,[ref]$tokens,[ref]$errors);if(@($errors).Count){throw ($errors|Out-String)} + $sample=[ordered]@{schema='scenario_spacing_oracle_build_v2';source_commit='8bf7607a3f3b53db0c36364f54f57674c64ea2f1';exit_code=0;inputs_unchanged=$true;postflight_failure=$null;binary=@{path='C:\probe\oracle.exe';bytes=100;sha256=('1'*64)};runtime=@{path='C:\probe\libunwind.dll';bytes=190464;sha256='aa282fa47bb2c4e210cea5e470cdbdca58568f0be0bc73e404d86ab9cd95ef6a'};runtime_source=@{path='C:\toolchain\libunwind.dll';bytes=190464;sha256='aa282fa47bb2c4e210cea5e470cdbdca58568f0be0bc73e404d86ab9cd95ef6a'}} + $expectedPin=$sample.source_commit + $json=$sample|ConvertTo-Json -Depth 8;$exe=$sample.binary;$runtime=$sample.runtime + Assert-AcceptedBuild ($json|ConvertFrom-Json) $exe $runtime $expectedPin + $revised=$json|ConvertFrom-Json;$revised.source_commit=('3'*40);Assert-AcceptedBuild $revised $exe $runtime ('3'*40) + $cases=@( + @{name='failed_compile';change={param($b)$b.exit_code=1}}, + @{name='changed_build_inputs';change={param($b)$b.inputs_unchanged=$false}}, + @{name='postflight_failure';change={param($b)$b.postflight_failure='changed'}}, + @{name='wrong_source';change={param($b)$b.source_commit=('0'*40)}}, + @{name='legacy_receipt';change={param($b)$b.schema='old'}}, + @{name='different_exe';change={param($b)$b.binary.sha256=('2'*64)}}, + @{name='different_exe_size';change={param($b)$b.binary.bytes=101}}, + @{name='different_exe_path';change={param($b)$b.binary.path='C:\other\oracle.exe'}}, + @{name='missing_runtime';change={param($b)$b.runtime=$null}}, + @{name='nonadjacent_runtime';change={param($b)$b.runtime.path='C:\other\libunwind.dll'}}, + @{name='wrong_runtime_hash';change={param($b)$b.runtime.sha256=('2'*64)}}, + @{name='wrong_runtime_size';change={param($b)$b.runtime.bytes=190463}}, + @{name='wrong_runtime_source';change={param($b)$b.runtime_source.sha256=('2'*64)}} + ) + $passed=@();foreach($case in $cases){$b=$json|ConvertFrom-Json;& $case.change $b;$rejected=$false;try{Assert-AcceptedBuild $b $exe $runtime $expectedPin}catch{$rejected=$true};if(-not $rejected){throw ('Guard admitted '+$case.name)};$passed+=@($case.name)} + foreach($change in @('path','sha256','bytes')){ + $r=($sample.runtime|ConvertTo-Json|ConvertFrom-Json) + switch($change){'path'{$r.path='C:\other\libunwind.dll'}'sha256'{$r.sha256=('3'*64)}'bytes'{$r.bytes=1}} + $rejected=$false;try{Assert-AcceptedBuild ($json|ConvertFrom-Json) $exe $r $expectedPin}catch{$rejected=$true};if(-not $rejected){throw 'Current runtime mutation admitted'};$passed+=@('current_runtime_'+$change) + } + $badPinRejected=$false;try{Assert-AcceptedBuild ($json|ConvertFrom-Json) $exe $runtime ''}catch{$badPinRejected=$true};if(-not $badPinRejected){throw 'Missing expected source pin admitted'};$passed+=@('missing_expected_source_pin') + [ordered]@{status='passed';positive_cases=2;negative_cases=$passed;compiler_executed=$false;oracle_executed=$false;scope='Pure production receipt/runtime admission function with synthetic metadata; no gate or binary execution'}|ConvertTo-Json -Depth 8 + return +} if(Test-Path -LiteralPath $Output){throw 'Preserve previous oracle evidence; choose a new output directory.'} if((Hash $OracleExe) -cne $OracleSha256 -or (Hash $BuildReceipt) -cne $BuildReceiptSha256){throw 'Reviewed oracle binary/build receipt binding differs.'} -$paths=@($OracleExe,$BuildReceipt,$PSCommandPath,(Join-Path $PSScriptRoot 'main.rs'),(Join-Path $PSScriptRoot '../incident-workspace.arcadia'),$Baseline) -if($Mode -ne 'self-check') {if([string]::IsNullOrWhiteSpace($Actual)){throw 'Actual checkpoint is required.'};$paths+=@($Actual)} -$before=@($paths|ForEach-Object {Record $_}) -$base=(Get-Item -LiteralPath $Baseline).FullName $exe=(Get-Item -LiteralPath $OracleExe).FullName -$outputPath=[IO.Path]::GetFullPath($Output) -[void][IO.Directory]::CreateDirectory($outputPath) -[IO.File]::Copy($base,(Join-Path $outputPath 'baseline.arcadia'),$false) -$argsForOracle=@($Mode,$base) -if($Mode -ne 'self-check'){ - $actualPath=(Get-Item -LiteralPath $Actual).FullName - [IO.File]::Copy($actualPath,(Join-Path $outputPath 'actual.arcadia'),$false) - $argsForOracle+=@($actualPath) +$build=Read-Json $BuildReceipt +$runtime=Join-Path (Split-Path -Parent $exe) 'libunwind.dll' +Assert-AcceptedBuild $build (Record $exe) (Record $runtime) $SourcePin +$buildDir=Split-Path -Parent ((Get-Item -LiteralPath $BuildReceipt).FullName) +$proof=@(@{name='inputs-before.json';sha=$build.before_manifest_sha256},@{name='inputs-after.json';sha=$build.after_manifest_sha256},@{name='toolchain.json';sha=$build.toolchain_sha256},@{name='compile.log';sha=$build.compile_log_sha256}) +foreach($f in $proof){if((Hash (Join-Path $buildDir $f.name)) -cne $f.sha){throw 'Build proof file changed'}} +$buildBefore=@(Read-Json (Join-Path $buildDir 'inputs-before.json'));$buildAfter=@(Read-Json (Join-Path $buildDir 'inputs-after.json')) +if(($buildBefore|ConvertTo-Json -Depth 8 -Compress) -cne ($buildAfter|ConvertTo-Json -Depth 8 -Compress)){throw 'Build input manifests differ'} +$byPath=@{};foreach($f in $buildBefore){if($byPath.ContainsKey($f.path)){throw 'Duplicate build manifest input'};$byPath[$f.path]=$f} +foreach($f in @($build.source_files)+@($build.runtime_source)){ + if(-not $byPath.ContainsKey($f.path) -or $byPath[$f.path].bytes -ne $f.bytes -or $byPath[$f.path].sha256 -cne $f.sha256){throw 'Source/runtime record not bound by successful build'} } -$argsForOracle+=@((Join-Path $outputPath 'project-comparison')) -if($Mode -in @('lane','message')){ +$localSource=@($PSCommandPath,(Join-Path $PSScriptRoot 'build.ps1'),(Join-Path $PSScriptRoot 'main.rs'),(Join-Path $PSScriptRoot '../incident-workspace.arcadia')) +foreach($p in $localSource){$path=(Get-Item -LiteralPath $p).FullName;$match=@($build.source_files|Where-Object {$_.path -ieq $path});if($match.Count -ne 1){throw 'Current oracle source is not the build-bound source'}} +Assert-Records $build.source_files +if((Hash (Join-Path $build.gate 'build-receipt.json')) -cne $build.gate_build_receipt_sha256){throw 'Accepted GUI gate receipt changed'} +# Runtime source is historical build input; the actual copied DLL is the loaded artifact. +$paths=@($exe,$runtime,$BuildReceipt,$Baseline)+$localSource+@($build.source_files|ForEach-Object path)+@($proof|ForEach-Object {Join-Path $buildDir $_.name})+@((Join-Path $build.gate 'build-receipt.json')) +if($Mode -ne 'self-check'){if([string]::IsNullOrWhiteSpace($Actual)){throw 'Actual checkpoint is required.'};$paths+=@($Actual)} +$extra=@();if($Mode -in @('lane','message')){ foreach($value in @($View,$Target,$Coordinate)){if([string]::IsNullOrWhiteSpace($value)){throw 'Spacing comparison needs independently chosen View, Target and Coordinate.'}} - $argsForOracle+=@($View,$Target,$Coordinate) -} elseif($View -or $Target -or $Coordinate){throw 'Unexpected spacing parameters for this comparison mode.'} -$start=[DateTime]::UtcNow.ToString('o') + $extra=@($View,$Target,$Coordinate) +}elseif($View -or $Target -or $Coordinate){throw 'Unexpected spacing parameters for this comparison mode.'} +$before=@($paths|ForEach-Object {(Get-Item -LiteralPath $_).FullName}|Sort-Object -Unique|ForEach-Object {Record $_}) +$outputPath=[IO.Path]::GetFullPath($Output);[void][IO.Directory]::CreateDirectory($outputPath) +$base=(Get-Item -LiteralPath $Baseline).FullName;$baseCopy=Join-Path $outputPath 'baseline.arcadia';[IO.File]::Copy($base,$baseCopy,$false) +$argsForOracle=@($Mode,$baseCopy) +if($Mode -ne 'self-check'){$actualPath=(Get-Item -LiteralPath $Actual).FullName;$actualCopy=Join-Path $outputPath 'actual.arcadia';[IO.File]::Copy($actualPath,$actualCopy,$false);$argsForOracle+=@($actualCopy)} +$argsForOracle+=@((Join-Path $outputPath 'project-comparison'))+$extra +Write-NewJson (Join-Path $outputPath 'inputs-before.json') $before +Write-NewJson (Join-Path $outputPath 'request.json') ([ordered]@{mode=$Mode;view=$View;target=$Target;coordinate=$Coordinate;expected_origin='Predetermined independently from actual pointer/scale and baseline; never learned from Actual'}) +$start=[DateTime]::UtcNow.ToString('o');$exit=$null;$failure=$null;$unchanged=$false;$semanticPass=$false $PSNativeCommandUseErrorActionPreference=$false -& $exe @argsForOracle *> (Join-Path $outputPath 'oracle.log') -$exit=$LASTEXITCODE -$unchanged=$true -foreach($file in $before){if((Get-Item -LiteralPath $file.path).Length -ne $file.bytes -or (Hash $file.path) -cne $file.sha256){$unchanged=$false}} -if((Hash (Join-Path $outputPath 'baseline.arcadia')) -cne (Hash $base)){$unchanged=$false} -if($Mode -ne 'self-check' -and (Hash (Join-Path $outputPath 'actual.arcadia')) -cne (Hash $actualPath)){$unchanged=$false} -$result=Join-Path $outputPath ('project-comparison/'+$(if($Mode -eq 'self-check'){'self-check.json'}else{'comparison.json'})) -$semanticPass=$false -if(Test-Path -LiteralPath $result){$record=Get-Content -Raw -LiteralPath $result|ConvertFrom-Json;$semanticPass=$record.status -ceq 'passed';if($Mode -ne 'self-check'){$semanticPass=$semanticPass -and $record.full_project_exact}} +try {& $exe @argsForOracle *> (Join-Path $outputPath 'oracle.log');$exit=$LASTEXITCODE}catch{$failure=$_.Exception.Message} +try { + Assert-Records $before + Assert-AcceptedBuild (Read-Json $BuildReceipt) (Record $exe) (Record $runtime) $SourcePin + if((Hash $baseCopy) -cne (Hash $base)){throw 'Baseline copy changed'} + if($Mode -ne 'self-check' -and (Hash $actualCopy) -cne (Hash $actualPath)){throw 'Actual copy changed'} + $after=@($before|ForEach-Object {Record $_.path});Write-NewJson (Join-Path $outputPath 'inputs-after.json') $after;$unchanged=$true + $result=Join-Path $outputPath ('project-comparison/'+$(if($Mode -eq 'self-check'){'self-check.json'}else{'comparison.json'})) + if(Test-Path -LiteralPath $result){$record=Read-Json $result;$semanticPass=$record.status -ceq 'passed';if($Mode -ne 'self-check'){$semanticPass=$semanticPass -and $record.mode -ceq $Mode -and $record.full_project_exact}} +}catch{if($null -eq $failure){$failure=$_.Exception.Message}} $files=@(Get-ChildItem -LiteralPath $outputPath -File -Recurse|ForEach-Object {Record $_.FullName}) -Write-NewJson (Join-Path $outputPath 'receipt.json') ([ordered]@{start_utc=$start;end_utc=[DateTime]::UtcNow.ToString('o');command=@($exe)+$argsForOracle;mode=$Mode;oracle_exit_code=$exit;inputs_unchanged=$unchanged;semantic_pass=$semanticPass;inputs_before=$before;output_files=$files;scope='External parsed saved-source Project comparison only; no native action or runtime/history inspection'}) -if($exit -ne 0 -or -not $unchanged -or -not $semanticPass){throw 'Oracle comparison or input stability failed; preserve receipt and raw output.'} +Write-NewJson (Join-Path $outputPath 'receipt.json') ([ordered]@{schema='scenario_spacing_oracle_run_v2';source_commit=$build.source_commit;start_utc=$start;end_utc=[DateTime]::UtcNow.ToString('o');command=@($exe)+$argsForOracle;mode=$Mode;oracle_exit_code=$exit;failure=$failure;inputs_unchanged=$unchanged;semantic_pass=$semanticPass;oracle_sha256=$OracleSha256;build_receipt_sha256=$BuildReceiptSha256;runtime_binding=$build.runtime;inputs_before=$before;output_files=$files;scope='External admitted saved-source Project comparison only; no native action or runtime/history inspection'}) +if($exit -ne 0 -or -not $unchanged -or -not $semanticPass -or $null -ne $failure){throw 'Oracle comparison or input stability failed; preserve receipt and raw output.'} Write-Output 'Saved-source oracle passed; runtime and native action evidence remain separate.'