Files
T

121 lines
8.4 KiB
PowerShell

#requires -Version 7.4
param(
[Parameter(Mandatory = $true)][string]$ArtifactMessages,
[Parameter(Mandatory = $true)][ValidatePattern('^[A-Za-z0-9_-]+$')][string]$RunName,
[ValidateRange(1,5)][int]$Iterations = 3,
[switch]$RunProbe
)
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $false
$repoRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
function ProjectPath([string]$Path) {
$full = [IO.Path]::GetFullPath($Path, $repoRoot)
if (-not $full.StartsWith($repoRoot + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) {
throw "Path escapes this project: $Path"
}
return $full
}
function Hash([string]$Path) { (Get-FileHash -Algorithm SHA256 -LiteralPath $Path).Hash.ToLowerInvariant() }
function WriteJson([string]$Path, $Value) {
if (Test-Path -LiteralPath $Path) { throw "Refusing overwrite: $Path" }
[IO.File]::WriteAllText($Path, ($Value | ConvertTo-Json -Depth 40) + "`n", [Text.UTF8Encoding]::new($false))
}
function VerifyInputs {
$manifest = Get-Content -Raw -Encoding UTF8 -LiteralPath (Join-Path $PSScriptRoot 'inputs.json') | ConvertFrom-Json
foreach ($entry in $manifest.files) {
$path = Join-Path $PSScriptRoot $entry.path
if ((Hash $path) -ne $entry.sha256 -or (Get-Item -LiteralPath $path).Length -ne $entry.bytes) {
throw "Frozen input changed: $($entry.path)"
}
}
}
$messagesPath = ProjectPath $ArtifactMessages
$runRoot = Join-Path $PSScriptRoot "runs/$RunName"
if (Test-Path -LiteralPath $runRoot) { throw "Exclusive run name already exists: $RunName" }
New-Item -ItemType Directory -Path $runRoot | Out-Null
try {
VerifyInputs
$messagesHash = Hash $messagesPath
$messages = @(Get-Content -Encoding UTF8 -LiteralPath $messagesPath | Where-Object { $_.Trim() } | ForEach-Object { $_ | ConvertFrom-Json -ErrorAction Stop })
$finished = @($messages | Where-Object { $_.reason -eq 'build-finished' })
if ($finished.Count -ne 1 -or -not $finished[0].success) { throw 'Require one successful terminal Cargo build-finished record' }
$bindings = @()
foreach ($crateName in @('oa_core','serde_json')) {
$artifacts = @($messages | Where-Object { $_.reason -eq 'compiler-artifact' -and $_.target.name -eq $crateName -and $_.target.kind -contains 'lib' -and -not $_.profile.test })
if ($artifacts.Count -ne 1) { throw "Require unique actual Cargo library artifact: $crateName" }
$artifact = $artifacts[0]
if ($crateName -eq 'oa_core' -and (ProjectPath $artifact.target.src_path) -ne (ProjectPath 'crates/oa-core/src/lib.rs')) {
throw 'oa_core artifact belongs to another checkout'
}
$rlibs = @($artifact.filenames | Where-Object { $_.EndsWith('.rlib') })
if ($rlibs.Count -ne 1) { throw "Require one emitted rlib for $crateName" }
$rlib = ProjectPath $rlibs[0]
$sha = Hash $rlib
$dir = Split-Path -Parent $rlib
$bound = $rlib
if ((Split-Path -Leaf $dir) -ne 'deps') {
$deps = Join-Path $dir 'deps'
$matchesByBytes = @(Get-ChildItem -LiteralPath $deps -Filter "lib$crateName-*.rlib" -File | Where-Object { (Hash $_.FullName) -eq $sha })
if ($matchesByBytes.Count -ne 1) { throw "No unique byte-hash binding for top-level $crateName rlib" }
$bound = $matchesByBytes[0].FullName
if (-not [Linq.Enumerable]::SequenceEqual[byte]([IO.File]::ReadAllBytes($rlib), [IO.File]::ReadAllBytes($bound))) { throw 'Artifact bytes differ despite hash match' }
$dir = $deps
}
$namePattern = '^lib' + [regex]::Escape($crateName) + '-([a-f0-9]+)\.rlib$'
if ((Split-Path -Leaf $bound) -notmatch $namePattern) { throw 'Cannot bind fingerprint from exact dependency filename' }
$suffix = $Matches[1]
$packageName = if ($crateName -eq 'oa_core') { 'oa-core' } else { 'serde_json' }
$fingerprint = Join-Path (Split-Path -Parent $dir) ".fingerprint/$packageName-$suffix"
$fingerprints = @()
foreach ($name in @("lib-$crateName", "lib-$crateName.json")) {
$original = Join-Path $fingerprint $name
$destination = Join-Path $runRoot "$crateName-$name"
Copy-Item -LiteralPath $original -Destination $destination
if ((Hash $original) -ne (Hash $destination)) { throw 'Fingerprint copy differs' }
$fingerprints += [ordered]@{ original = $original; copy = $destination; sha256 = Hash $original }
}
$bindings += [ordered]@{ crate = $crateName; artifact = $artifact; rlib = $rlib; dependencyRlib = $bound; sha256 = $sha; dependencyDirectory = $dir; fingerprints = $fingerprints }
}
$deps = @($bindings.dependencyDirectory | Sort-Object -Unique)
if ($deps.Count -ne 1) { throw 'Dependency artifacts do not share a directory' }
$rustc = Join-Path $repoRoot '.local/toolchain/rustup/toolchains/stable-x86_64-pc-windows-gnullvm/bin/rustc.exe'
$linker = Join-Path $repoRoot '.local/toolchain/llvm-mingw-20260826-ucrt-x86_64/bin/clang.exe'
$runtime = Join-Path (Split-Path -Parent $linker) 'libunwind.dll'
$env:PATH = "$(Split-Path -Parent $rustc);$(Split-Path -Parent $linker);$env:SystemRoot\System32;$env:SystemRoot"
$env:CARGO_BUILD_JOBS = '1'
$env:RUST_BACKTRACE = '0'
Copy-Item -LiteralPath $messagesPath -Destination (Join-Path $runRoot 'cargo-artifacts.jsonl')
if ((Hash $messagesPath) -ne $messagesHash -or (Hash (Join-Path $runRoot 'cargo-artifacts.jsonl')) -ne $messagesHash) { throw 'Cargo messages changed during binding' }
WriteJson (Join-Path $runRoot 'bindings.json') ([ordered]@{ artifactMessages = $messagesPath; artifactMessagesSha256 = $messagesHash; bindings = $bindings; compiler = $rustc; compilerSha256 = Hash $rustc; linker = $linker; linkerSha256 = Hash $linker; runtime = $runtime; runtimeSha256 = Hash $runtime; inputsSha256 = Hash (Join-Path $PSScriptRoot 'inputs.json'); runnerSha256 = Hash $PSCommandPath })
function Recorded([string]$Name, [string]$Executable, [string[]]$Arguments) {
$stdout = Join-Path $runRoot "$Name.stdout.log"
$stderr = Join-Path $runRoot "$Name.stderr.log"
$receipt = Join-Path $runRoot "$Name.status.json"
foreach ($file in @($stdout,$stderr,$receipt)) { if (Test-Path -LiteralPath $file) { throw "Refusing overwrite $file" } }
$start = [DateTime]::UtcNow
$clock = [Diagnostics.Stopwatch]::StartNew()
& $Executable @Arguments 1> $stdout 2> $stderr
$code = $LASTEXITCODE
$clock.Stop()
WriteJson $receipt ([ordered]@{ startedUtc = $start.ToString('o'); finishedUtc = [DateTime]::UtcNow.ToString('o'); exitCode = $code; wallSeconds = $clock.Elapsed.TotalSeconds; executable = $Executable; executableSha256 = Hash $Executable; arguments = $Arguments; stdoutSha256 = Hash $stdout; stderrSha256 = Hash $stderr })
if ($code -ne 0) { throw "Recorded phase $Name failed with exit $code" }
}
Recorded 'compiler-version' $rustc @('--version','--verbose')
$binary = Join-Path $runRoot 'alias-document-probe.exe'
$compileArguments = @('--edition=2024','--crate-name','alias_document_probe','-Dwarnings','-C','opt-level=0','-C','debug-assertions=yes','-C',"linker=$linker",(Join-Path $PSScriptRoot 'probe.rs'),'-L',"dependency=$($deps[0])",'-o',$binary)
foreach ($binding in $bindings) { $compileArguments += @('--extern',"$($binding.crate)=$($binding.rlib)") }
Recorded 'compile' $rustc $compileArguments
VerifyInputs
foreach ($binding in $bindings) {
if ((Hash $binding.rlib) -ne $binding.sha256 -or (Hash $binding.dependencyRlib) -ne $binding.sha256) { throw 'Bound dependency changed during compilation' }
}
if ($RunProbe) {
Recorded 'probe' $binary @((Join-Path $PSScriptRoot 'inputs/release1-baseline/project.arcadia'),(Join-Path $PSScriptRoot 'inputs/cli-preview.json'),$Iterations.ToString())
VerifyInputs
}
WriteJson (Join-Path $runRoot 'result.json') ([ordered]@{ compiled = $true; probeExecuted = [bool]$RunProbe; binarySha256 = Hash $binary; allFrozenInputsUnchanged = $true; scope = 'Private copied Document with actual bound core artifacts; no UI, persistence writes or prepared-capability benchmark' })
} catch {
WriteJson (Join-Path $runRoot 'failure.json') ([ordered]@{ atUtc = [DateTime]::UtcNow.ToString('o'); error = $_.ToString(); scope = 'Only recorded phase exits establish execution; never overwrite or silently retry this run name' })
throw
}