Files
ARCADIA-Studio/harness/experiments/scenario-comparators-gui-b10b28c-20260913/spacing/oracle/build.ps1
T

183 lines
15 KiB
PowerShell

#Requires -Version 7.0
[CmdletBinding(DefaultParameterSetName='Build')]
param(
[Parameter(Mandatory=$true,ParameterSetName='Build')][ValidatePattern('^[0-9a-f]{40}$')][string]$SourcePin,
[Parameter(Mandatory=$true,ParameterSetName='Build')][string]$Gate,
[Parameter(Mandatory=$true,ParameterSetName='Build')][string]$CoreRlib,
[Parameter(Mandatory=$true,ParameterSetName='Build')][string]$SerdeJsonRlib,
[Parameter(Mandatory=$true,ParameterSetName='Build')][string]$WindowsImportLibDirectory,
[Parameter(Mandatory=$true,ParameterSetName='Build')][string]$Output,
[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
$ErrorActionPreference='Stop'
function Fingerprint-Value([string]$Hex) {
if($Hex -cnotmatch '^[0-9a-f]{16}$'){throw 'Cargo fingerprint must be a complete 16-hex sidecar.'}
$bytes=[Convert]::FromHexString($Hex)
if(-not [BitConverter]::IsLittleEndian){[Array]::Reverse($bytes)}
[BitConverter]::ToUInt64($bytes,0)
}
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'))){
$tokens=$null;$errors=$null
[void][Management.Automation.Language.Parser]::ParseFile($script,[ref]$tokens,[ref]$errors)
if(@($errors).Count){throw ($errors|Out-String)}
}
$fingerprint=Fingerprint-Value 'ab2fa54704e8737e'
if($fingerprint -ne [UInt64]9111881576180166571){throw 'Little-endian fingerprint oracle failed.'}
$sample='{"deps":[[1,"serde_json",false,9111881576180166571]]}'|ConvertFrom-Json
Assert-Dependency $sample 'serde_json' $fingerprint
if((Fingerprint-Value 'ffffffffffffffff') -ne [UInt64]::MaxValue){throw 'Full-width unsigned fingerprint oracle failed.'}
Assert-Dependency ('{"deps":[[1,"oa_core",false,18446744073709551615]]}'|ConvertFrom-Json) 'oa_core' ([UInt64]::MaxValue)
$rejected=0
foreach($probe in @(
{Fingerprint-Value ''},
{Fingerprint-Value 'not-a-fingerprint'},
{Assert-Dependency $sample 'oa_core' $fingerprint},
{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.'}
$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 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
$expected=Read-NativeJson (Join-Path $Gate 'expected-inputs.json')
$nativeInputCount=[int]$expected.count
$build=Assert-BuildEvidence $Gate
Assert-CurrentNativeSource $Gate
if(Test-Path -LiteralPath $Output){throw 'Preserve existing build evidence; Output must be new.'}
$Output=[IO.Path]::GetFullPath($Output)
$deps=[IO.Path]::GetFullPath((Join-Path $build.target 'debug/deps'))
$fingerprints=[IO.Path]::GetFullPath((Join-Path $build.target 'debug/.fingerprint'))
$core=(Get-Item -LiteralPath $CoreRlib).FullName
$serde=(Get-Item -LiteralPath $SerdeJsonRlib).FullName
if([IO.Path]::GetDirectoryName($core) -ine $deps -or [IO.Path]::GetDirectoryName($serde) -ine $deps){throw 'Explicit rlibs must belong to the gated target dependency directory.'}
if([IO.Path]::GetFileName($core) -cnotmatch '^liboa_core-([0-9a-f]{16})\.rlib$'){throw 'Expected an actual oa-core rlib.'}
$coreSuffix=$Matches[1]
if([IO.Path]::GetFileName($serde) -cnotmatch '^libserde_json-([0-9a-f]{16})\.rlib$'){throw 'Expected an actual serde_json rlib.'}
$serdeSuffix=$Matches[1]
$coreFingerprint=Join-Path $fingerprints ('oa-core-'+$coreSuffix)
$serdeFingerprint=Join-Path $fingerprints ('serde_json-'+$serdeSuffix)
$coreRecord=Read-NativeJson (Join-Path $coreFingerprint 'lib-oa_core.json')
$coreValue=Fingerprint-Value ((Get-Content -Raw -LiteralPath (Join-Path $coreFingerprint 'lib-oa_core')).Trim())
$serdeValue=Fingerprint-Value ((Get-Content -Raw -LiteralPath (Join-Path $serdeFingerprint 'lib-serde_json')).Trim())
Assert-Dependency $coreRecord 'serde_json' $serdeValue
$matching=@(foreach($folder in Get-ChildItem -LiteralPath $fingerprints -Directory -Filter 'oa-desktop-*') {
$json=Join-Path $folder.FullName 'bin-arcadia-studio.json'
if(-not (Test-Path -LiteralPath $json)){continue}
$suffix=$folder.Name.Substring('oa-desktop-'.Length)
$exe=Join-Path $deps ('arcadia_studio-'+$suffix+'.exe')
if(-not (Test-Path -LiteralPath $exe)){continue}
if((Get-Item -LiteralPath $exe).Length -eq $build.binary.bytes -and (Get-NativeHash $exe) -ceq $build.binary.sha256){[pscustomobject]@{folder=$folder.FullName;exe=$exe;json=$json;suffix=$suffix}}
})
if($matching.Count -ne 1){throw 'The successful GUI binary must identify exactly one production Cargo fingerprint; do not guess a dependency chain.'}
$desktop=$matching[0]
$desktopRecord=Read-NativeJson $desktop.json
Assert-Dependency $desktopRecord 'oa_core' $coreValue
if((Get-NativeHash $build.binary.path) -cne $build.binary.sha256 -or (Get-Item -LiteralPath $build.binary.path).Length -ne $build.binary.bytes){throw 'Current GUI artifact changed since successful gate.'}
$native=(Resolve-Path -LiteralPath $WindowsImportLibDirectory).Path
if(-not (Test-Path -LiteralPath $native -PathType Container)){throw 'Native import-library directory is required.'}
if(-not @(Get-ChildItem -LiteralPath $native -File -Recurse|Where-Object {$_.Extension -in @('.a','.lib')}).Count){throw 'No actual native import libraries found.'}
# Gate already binds this process-local environment script and external configuration.
. $build.externalEnvironmentPath
$rustc=(Get-Command rustc -CommandType Application).Source
$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=(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=@($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','.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
$clangVersion=@(& $clang --version 2>&1|ForEach-Object {$_.ToString()});$clangVersionExit=$LASTEXITCODE
Write-NativeJson (Join-Path $Output 'toolchain.json') ([ordered]@{rustc=$rustc;rustc_version=$rustcVersion;rustc_version_exit=$rustcVersionExit;clang=$clang;clang_version=$clangVersion;clang_version_exit=$clangVersionExit})
if($rustcVersionExit -ne 0 -or $clangVersionExit -ne 0){throw 'Actual compiler/linker version query failed; preserve evidence.'}
if(($rustcVersion -join "`n") -cne (@($gateVersions.rustc) -join "`n") -or ($clangVersion -join "`n") -cne (@($gateVersions.clang) -join "`n")){throw 'Actual compiler/linker versions differ from the successful gate.'}
$exe=Join-Path $Output 'incident-source-oracle.exe'
$compilerArgs=@('--edition=2024','-D','warnings',$source,'--extern',('oa_core='+$core),'--extern',('serde_json='+$serde),'-L',('dependency='+$deps),'-L',('native='+$native),'-C',('linker='+$clang),'-o',$exe)
$start=[DateTime]::UtcNow.ToString('o')
& $rustc @compilerArgs *> (Join-Path $Output 'compile.log')
$exit=$LASTEXITCODE
$end=[DateTime]::UtcNow.ToString('o')
$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]@{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.'