93 lines
8.1 KiB
PowerShell
93 lines
8.1 KiB
PowerShell
param([Parameter(Mandatory)][string]$Original, [Parameter(Mandatory)][string]$Review)
|
|
$ErrorActionPreference = 'Stop'
|
|
$destination = $PSScriptRoot
|
|
$Original = (Resolve-Path -LiteralPath $Original).Path
|
|
function Json($path) { Get-Content -LiteralPath $path -Raw | ConvertFrom-Json }
|
|
function Hash($path) { (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() }
|
|
function AssertFile($root, $record) {
|
|
$path = Join-Path $root $record.path
|
|
if ((Get-Item -LiteralPath $path).Length -ne $record.bytes -or (Hash $path) -ne $record.sha256) { throw "File mismatch: $path" }
|
|
}
|
|
$inputRecord = Json "$Original/input-manifest.json"
|
|
foreach ($file in $inputRecord.immutable_files) { AssertFile $Original $file }
|
|
if ($inputRecord.immutable_files.Count -ne 15) { throw 'Expected 15 immutable inputs' }
|
|
$launch = Json "$Original/launch.json"
|
|
$close = Json "$Original/normal-close.json"
|
|
if ($launch.manifest_sha256 -ne (Hash "$Original/input-manifest.json") -or $launch.script_sha256 -ne (Hash "$Original/launch.ps1") -or $launch.binary_sha256 -ne $inputRecord.binary_copy_sha256 -or $launch.pid -ne $close.pid -or $launch.started_utc -ne $close.started_utc) { throw 'Launch binding mismatch' }
|
|
$before = Json "$Original/evidence/inputs-complete-before.json"
|
|
$after = Json "$Original/evidence/inputs-complete-after.json"
|
|
if ($before.count -ne 577 -or $after.count -ne 577 -or ($before.files | ConvertTo-Json -Depth 8 -Compress) -cne ($after.files | ConvertTo-Json -Depth 8 -Compress)) { throw 'Build input manifest mismatch' }
|
|
foreach ($file in $after.files) { AssertFile $after.workspace $file }
|
|
if ($close.pid -ne 1352 -or $close.exit_code -ne 0 -or -not $close.immutable_inputs_unchanged) { throw 'Unexpected closure' }
|
|
foreach ($file in Get-ChildItem -LiteralPath "$Original/logs" -File) { if ($file.Length -ne 0) { throw 'Expected empty logs' } }
|
|
$captures = @(Get-ChildItem -LiteralPath "$Original/captures" -Filter '*.json' -File)
|
|
if ($captures.Count -ne 44) { throw 'Expected 44 capture records' }
|
|
foreach ($capture in $captures) {
|
|
foreach ($screen in (Json $capture.FullName).screenshots) {
|
|
AssertFile $Original $screen
|
|
$bytes = [IO.File]::ReadAllBytes((Join-Path $Original $screen.path))
|
|
if ($screen.mime -ne 'image/jpeg' -or $bytes[0] -ne 255 -or $bytes[1] -ne 216 -or $bytes[2] -ne 255) { throw 'Unexpected capture format' }
|
|
}
|
|
}
|
|
$actions = Json "$Original/action-observations.json"
|
|
$actionNames = @($actions.observations.capture | Sort-Object)
|
|
$captureNames = @($captures | ForEach-Object { 'captures/' + $_.Name } | Sort-Object)
|
|
if (($actionNames -join "`n") -cne ($captureNames -join "`n")) { throw 'Action/capture membership differs' }
|
|
foreach ($kind in @('seed','baseline')) {
|
|
foreach ($file in (Json "$Original/$kind-manifest.json").files) { AssertFile "$Original/$kind" $file }
|
|
}
|
|
$snapshot = Json "$Original/snapshots/renamed-before-model-undo.json"
|
|
$sourceChecks = @()
|
|
foreach ($file in (Json "$Original/baseline-manifest.json").files) {
|
|
$baseline = [IO.File]::ReadAllText((Join-Path "$Original/baseline" $file.path))
|
|
$expected = [regex]::Replace($baseline, '\bassess_report\b', 'assess_report_union')
|
|
$edits = [regex]::Matches($baseline, '\bassess_report\b').Count
|
|
if ($snapshot.accepted.files.($file.path) -cne $expected) { throw "Snapshot mismatch: $($file.path)" }
|
|
foreach ($dir in @('fixture','oracle-model-equivalence-20260913/accepted','oracle-model-equivalence-20260913/command-renamed')) {
|
|
$actualBytes = [IO.File]::ReadAllBytes((Join-Path "$Original/$dir" $file.path))
|
|
$expectedBytes = [Text.UTF8Encoding]::new($false).GetBytes($expected)
|
|
if ([Convert]::ToBase64String($actualBytes) -cne [Convert]::ToBase64String($expectedBytes)) { throw "Source mismatch: $dir/$($file.path)" }
|
|
}
|
|
if ((Hash (Join-Path "$Original/baseline" $file.path)) -ne (Hash (Join-Path "$Original/oracle-model-equivalence-20260913/baseline" $file.path))) { throw 'CLI baseline mismatch' }
|
|
$sourceChecks += [ordered]@{path=$file.path; substitutions=$edits; snapshotAndFinalAndCliExact=$true}
|
|
}
|
|
if (($sourceChecks.substitutions | Measure-Object -Sum).Sum -ne 6) { throw 'Expected six substitutions' }
|
|
$cli = Json "$Original/oracle-model-equivalence-20260913/receipt.json"
|
|
if ($cli.commands.Count -ne 6 -or @($cli.commands | Where-Object exit_code -ne 0).Count -or $cli.cli_sha256_before -ne $cli.cli_sha256_after) { throw 'CLI receipt mismatch' }
|
|
$checks = @('check-baseline','check-accepted','check-command-copy') | ForEach-Object { Json "$Original/oracle-model-equivalence-20260913/$_.stdout.txt" }
|
|
foreach ($check in $checks) { if (@($check.diagnostics | Where-Object severity -eq 'error').Count -or @($check.diagnostics | Where-Object severity -eq 'warning').Count -ne 11) { throw 'Unexpected CLI diagnostics' } }
|
|
$diagnosticSets = @($checks | ForEach-Object { ,@($_.diagnostics | ForEach-Object { @($_.severity,$_.code,$_.object_id,$_.field,$_.message) -join '|' } | Sort-Object) })
|
|
foreach ($set in $diagnosticSets) { if (($set -join "`n") -cne ($diagnosticSets[0] -join "`n")) { throw 'Diagnostic identities/messages changed' } }
|
|
$copied = @()
|
|
$originalFiles = @(Get-ChildItem -LiteralPath $Original -File)
|
|
foreach ($dir in @('seed','baseline','fixture','snapshots','evidence','logs','oracle-model-equivalence-20260913')) { $originalFiles += @(Get-ChildItem -LiteralPath "$Original/$dir" -Recurse -File) }
|
|
$originalFiles += $captures
|
|
foreach ($file in $originalFiles) {
|
|
$relative = [IO.Path]::GetRelativePath($Original,$file.FullName).Replace('\','/')
|
|
$target = Join-Path $destination $relative
|
|
[IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($target)) | Out-Null
|
|
if (Test-Path -LiteralPath $target) { throw "Do not overwrite archive: $target" }
|
|
Copy-Item -LiteralPath $file.FullName -Destination $target
|
|
if ((Hash $file.FullName) -ne (Hash $target)) { throw 'Copy mismatch' }
|
|
$copied += [ordered]@{path=$relative; bytes=$file.Length; sha256=(Hash $target)}
|
|
}
|
|
Copy-Item -LiteralPath $Review -Destination "$destination/independent-review.md"
|
|
if ((Hash $Review) -ne (Hash "$destination/independent-review.md")) { throw 'Review copy mismatch' }
|
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
[IO.Compression.ZipFile]::CreateFromDirectory("$Original/captures", "$destination/captures.zip", [IO.Compression.CompressionLevel]::Optimal, $false)
|
|
$zip = [IO.Compression.ZipFile]::OpenRead("$destination/captures.zip")
|
|
$captureManifest = @()
|
|
try {
|
|
if ($zip.Entries.Count -ne 88) { throw 'Expected 88 capture ZIP entries' }
|
|
foreach ($entry in $zip.Entries) {
|
|
$stream = $entry.Open()
|
|
try { $sha = [Security.Cryptography.SHA256]::Create(); try { $digest = [Convert]::ToHexString($sha.ComputeHash($stream)).ToLowerInvariant() } finally { $sha.Dispose() } } finally { $stream.Dispose() }
|
|
$originalFile = Get-Item -LiteralPath (Join-Path "$Original/captures" $entry.FullName)
|
|
if ($entry.Length -ne $originalFile.Length -or $digest -ne (Hash $originalFile.FullName)) { throw 'ZIP mismatch' }
|
|
$captureManifest += [ordered]@{path=$entry.FullName; bytes=$entry.Length; sha256=$digest}
|
|
}
|
|
} finally { $zip.Dispose() }
|
|
$captureManifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath "$destination/capture-manifest.json" -Encoding utf8
|
|
[ordered]@{verifiedUtc=[DateTime]::UtcNow.ToString('o'); source=$inputRecord.source_commit; immutableInputsChecked=15; currentBuildInputsChecked=577; beforeAfterBuildInputsEqual=$true; capturesChecked=44; zipEntriesChecked=88; actionMappingsChecked=44; binaryBytes=115379712; binarySha256=$inputRecord.binary_copy_sha256; closeExitCode=$close.exit_code; sourceChecks=$sourceChecks; cliCommandsPassed=6; cliWarningsPerCheck=11; cliDiagnosticIdentitiesAndMessagesEqual=$true; reviewSha256=(Hash $Review); copiedFiles=$copied; limits='Checks preserved source bytes, receipts and artifacts; no compiler, native inputs, or direct runtime Project export executed by archivist.'} | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath "$destination/verification.json" -Encoding utf8
|
|
Write-Output "Verified 15 immutable inputs, 44 JPEGs, 88 ZIP entries, 44 action mappings, six source files, six CLI receipts; copied $($copied.Count) raw files."
|